mirror of
https://github.com/JoinMarket-Org/joinmarket-clientserver.git
synced 2026-08-13 12:33:40 +02:00
122 lines
4.9 KiB
Python
Executable file
122 lines
4.9 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""A simple command line tool to create a bunch
|
|
of utxos from one (thus giving more potential commitments
|
|
for a Joinmarket user, although of course it may be useful
|
|
for other reasons).
|
|
"""
|
|
|
|
from optparse import OptionParser
|
|
import jmbitcoin as btc
|
|
from jmbase import (get_log, jmprint, bintohex, utxostr_to_utxo,
|
|
IndentedHelpFormatterWithNL, cli_prompt_user_yesno)
|
|
from jmclient import load_program_config, estimate_tx_fee, jm_single,\
|
|
validate_address, get_utxo_info, add_base_options,\
|
|
validate_utxo_data, quit, BTCEngine, compute_tx_locktime
|
|
|
|
log = get_log()
|
|
|
|
def sign(utxo, priv, destaddrs, utxo_address_type):
|
|
"""Sign a tx sending the amount amt, from utxo utxo,
|
|
equally to each of addresses in list destaddrs,
|
|
after fees; the purpose is to create multiple utxos.
|
|
utxo_address_type must be one of p2sh-p2wpkh/p2wpkh/p2pkh.
|
|
"""
|
|
results = validate_utxo_data([(utxo, priv)], retrieve=True,
|
|
utxo_address_type=utxo_address_type)
|
|
if not results:
|
|
return False
|
|
assert results[0][0] == utxo
|
|
amt = results[0][1]
|
|
ins = [utxo]
|
|
estfee = estimate_tx_fee(1, len(destaddrs), txtype=utxo_address_type)
|
|
outs = []
|
|
share = int((amt - estfee) / len(destaddrs))
|
|
fee = amt - share*len(destaddrs)
|
|
assert fee >= estfee
|
|
log.info("Using fee: " + str(fee))
|
|
for i, addr in enumerate(destaddrs):
|
|
outs.append({'address': addr, 'value': share})
|
|
tx = btc.make_shuffled_tx(ins, outs, version=2, locktime=compute_tx_locktime())
|
|
amtforsign = amt if utxo_address_type != "p2pkh" else None
|
|
rawpriv, _ = BTCEngine.wif_to_privkey(priv)
|
|
if utxo_address_type == "p2wpkh":
|
|
native = utxo_address_type
|
|
else:
|
|
native = False
|
|
success, msg = btc.sign(tx, 0, rawpriv, amount=amtforsign, native=native)
|
|
assert success, msg
|
|
return tx
|
|
|
|
description="""For creating multiple utxos from one (for commitments in JM).
|
|
Provide a utxo in form txid:N that has some unspent coins;
|
|
Specify a list of destination addresses and the coins will
|
|
be split equally between them (after bitcoin fees).
|
|
You'll be prompted to enter the private key for the utxo
|
|
during the run; it must be in WIF compressed format.
|
|
After the transaction is completed, the utxo strings for
|
|
the new outputs will be shown.
|
|
Note that these utxos will not be ready for use as external
|
|
commitments in Joinmarket until 5 confirmations have passed.
|
|
BE CAREFUL about handling private keys!
|
|
Don't do this in insecure environments.
|
|
Works only with p2pkh ('1'), p2sh-p2wpkh (segwit '3') or
|
|
p2wpkh ('bc1') addresses.
|
|
utxos - set segwit=False in the POLICY section of
|
|
joinmarket.cfg for the former."""
|
|
|
|
def main():
|
|
parser = OptionParser(
|
|
usage=
|
|
'usage: %prog [options] utxo destaddr1 destaddr2 ..',
|
|
description=description, formatter=IndentedHelpFormatterWithNL())
|
|
parser.add_option(
|
|
'-t',
|
|
'--utxo-address-type',
|
|
action='store',
|
|
dest='utxo_address_type',
|
|
help=('type of address of coin being spent - one of "p2pkh", "p2wpkh", "p2sh-p2wpkh". '
|
|
'No other scriptpubkey types (e.g. multisig) are supported. If not set, we default '
|
|
'to what is in joinmarket.cfg.'),
|
|
default=""
|
|
)
|
|
add_base_options(parser)
|
|
(options, args) = parser.parse_args()
|
|
load_program_config(config_path=options.datadir)
|
|
if len(args) < 2:
|
|
quit(parser, 'Invalid syntax')
|
|
u = args[0]
|
|
priv = input(
|
|
'input private key for ' + u + ', in WIF compressed format : ')
|
|
u, priv = get_utxo_info(','.join([u, priv]))
|
|
if not u:
|
|
quit(parser, "Failed to parse utxo info: " + u)
|
|
destaddrs = args[1:]
|
|
for d in destaddrs:
|
|
if not validate_address(d):
|
|
quit(parser, "Address was not valid; wrong network?: " + d)
|
|
success, utxo = utxostr_to_utxo(u)
|
|
if not success:
|
|
quit(parser, "Failed to load utxo from string: " + utxo)
|
|
if options.utxo_address_type == "":
|
|
if jm_single().config.get("POLICY", "segwit") == "false":
|
|
utxo_address_type = "p2pkh"
|
|
elif jm_single().config.get("POLICY", "native") == "false":
|
|
utxo_address_type = "p2sh-p2wpkh"
|
|
else:
|
|
utxo_address_type = "p2wpkh"
|
|
else:
|
|
utxo_address_type = options.utxo_address_type
|
|
txsigned = sign(utxo, priv, destaddrs, utxo_address_type)
|
|
if not txsigned:
|
|
log.info("Transaction signing operation failed, see debug messages for details.")
|
|
return
|
|
log.info("Got signed transaction:\n" + bintohex(txsigned.serialize()))
|
|
log.info(btc.human_readable_transaction(txsigned))
|
|
if not cli_prompt_user_yesno('Would you like to push to the network?'):
|
|
log.info("You chose not to broadcast the transaction, quitting.")
|
|
return
|
|
jm_single().bc_interface.pushtx(txsigned.serialize())
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
jmprint('done', "success")
|