|
|
|
|
@ -11,26 +11,24 @@ import jmbitcoin as btc
|
|
|
|
|
from jmbase import get_log, jmprint, bintohex, utxostr_to_utxo |
|
|
|
|
from jmclient import load_program_config, estimate_tx_fee, jm_single,\ |
|
|
|
|
validate_address, get_utxo_info, add_base_options,\ |
|
|
|
|
validate_utxo_data, quit, BTCEngine |
|
|
|
|
validate_utxo_data, quit, BTCEngine, compute_tx_locktime |
|
|
|
|
|
|
|
|
|
log = get_log() |
|
|
|
|
|
|
|
|
|
def sign(utxo, priv, destaddrs, segwit=True): |
|
|
|
|
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 a large |
|
|
|
|
number of utxos. If segwit=True the (single) utxo is assumed to |
|
|
|
|
be of type segwit p2sh/p2wpkh. |
|
|
|
|
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, segwit=segwit) |
|
|
|
|
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] |
|
|
|
|
# TODO extend to other utxo types |
|
|
|
|
txtype = 'p2sh-p2wpkh' if segwit else 'p2pkh' |
|
|
|
|
estfee = estimate_tx_fee(1, len(destaddrs), txtype=txtype) |
|
|
|
|
estfee = estimate_tx_fee(1, len(destaddrs), txtype=utxo_address_type) |
|
|
|
|
outs = [] |
|
|
|
|
share = int((amt - estfee) / len(destaddrs)) |
|
|
|
|
fee = amt - share*len(destaddrs) |
|
|
|
|
@ -38,10 +36,14 @@ def sign(utxo, priv, destaddrs, segwit=True):
|
|
|
|
|
log.info("Using fee: " + str(fee)) |
|
|
|
|
for i, addr in enumerate(destaddrs): |
|
|
|
|
outs.append({'address': addr, 'value': share}) |
|
|
|
|
tx = btc.mktx(ins, outs) |
|
|
|
|
amtforsign = amt if segwit else None |
|
|
|
|
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) |
|
|
|
|
success, msg = btc.sign(tx, 0, rawpriv, amount=amtforsign) |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
@ -69,28 +71,14 @@ def main():
|
|
|
|
|
" joinmarket.cfg for the former." |
|
|
|
|
) |
|
|
|
|
parser.add_option( |
|
|
|
|
'-v', |
|
|
|
|
'--validate-utxos', |
|
|
|
|
action='store_true', |
|
|
|
|
dest='validate', |
|
|
|
|
help='validate the utxos and pubkeys provided against the blockchain', |
|
|
|
|
default=False |
|
|
|
|
) |
|
|
|
|
parser.add_option( |
|
|
|
|
'-o', |
|
|
|
|
'--validate-only', |
|
|
|
|
action='store_true', |
|
|
|
|
dest='vonly', |
|
|
|
|
help='only validate the provided utxos (file or command line), not add', |
|
|
|
|
default=False |
|
|
|
|
) |
|
|
|
|
parser.add_option( |
|
|
|
|
'-n', |
|
|
|
|
'--non-segwit-input', |
|
|
|
|
action='store_true', |
|
|
|
|
dest='nonsegwit', |
|
|
|
|
help='input is p2pkh ("1" address), not segwit; if not used, input is assumed to be segwit type.', |
|
|
|
|
default=False |
|
|
|
|
'-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() |
|
|
|
|
@ -110,13 +98,21 @@ def main():
|
|
|
|
|
success, utxo = utxostr_to_utxo(u) |
|
|
|
|
if not success: |
|
|
|
|
quit(parser, "Failed to load utxo from string: " + utxo) |
|
|
|
|
txsigned = sign(utxo, priv, destaddrs, segwit = not options.nonsegwit) |
|
|
|
|
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.debug("Deserialized:") |
|
|
|
|
log.debug(pformat(str(txsigned))) |
|
|
|
|
log.info(btc.human_readable_transaction(txsigned)) |
|
|
|
|
if input('Would you like to push to the network? (y/n):')[0] != 'y': |
|
|
|
|
log.info("You chose not to broadcast the transaction, quitting.") |
|
|
|
|
return |
|
|
|
|
|