Browse Source

Support output of PSBT instead of broadcast in direct_send

master
Adam Gibson 6 years ago
parent
commit
de3ad53226
No known key found for this signature in database
GPG Key ID: 141001A1AF77F20B
  1. 8
      jmclient/jmclient/cli_options.py
  2. 86
      jmclient/jmclient/taker_utils.py
  3. 7
      scripts/sendpayment.py

8
jmclient/jmclient/cli_options.py

@ -519,6 +519,14 @@ def get_sendpayment_parser():
help='specify recipient IRC nick for a ' help='specify recipient IRC nick for a '
'p2ep style payment, for example:\n' 'p2ep style payment, for example:\n'
'J5Ehn3EieVZFtm4q ') 'J5Ehn3EieVZFtm4q ')
parser.add_option('--psbt',
action='store_true',
dest='with_psbt',
default=False,
help='output as psbt instead of '
'broadcasting the transaction. '
'Currently only works with direct '
'send (-N 0).')
add_common_options(parser) add_common_options(parser)
return parser return parser

86
jmclient/jmclient/taker_utils.py

@ -11,7 +11,8 @@ from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\
schedule_to_text schedule_to_text
from .wallet import BaseWallet, estimate_tx_fee, compute_tx_locktime, \ from .wallet import BaseWallet, estimate_tx_fee, compute_tx_locktime, \
FidelityBondMixin FidelityBondMixin
from jmbitcoin import make_shuffled_tx, amount_to_str, mk_burn_script from jmbitcoin import make_shuffled_tx, amount_to_str, mk_burn_script,\
PartiallySignedTransaction, CMutableTxOut
from jmbase.support import EXIT_SUCCESS from jmbase.support import EXIT_SUCCESS
log = get_log() log = get_log()
@ -22,7 +23,7 @@ Currently re-used by CLI script tumbler.py and joinmarket-qt
def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False, def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
accept_callback=None, info_callback=None, accept_callback=None, info_callback=None,
return_transaction=False): return_transaction=False, with_final_psbt=False):
"""Send coins directly from one mixdepth to one destination address; """Send coins directly from one mixdepth to one destination address;
does not need IRC. Sweep as for normal sendpayment (set amount=0). does not need IRC. Sweep as for normal sendpayment (set amount=0).
If answeryes is True, callback/command line query is not performed. If answeryes is True, callback/command line query is not performed.
@ -39,8 +40,13 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
pushed), and returns nothing. pushed), and returns nothing.
This function returns: This function returns:
The txid if transaction is pushed, False otherwise, 1. False if there is any failure.
or the full CMutableTransaction if return_transaction is True. 2. The txid if transaction is pushed, and return_transaction is False,
and with_final_psbt is False.
3. The full CMutableTransaction is return_transaction is True and
with_final_psbt is False.
4. The PSBT object if with_final_psbt is True, and in
this case the transaction is *NOT* broadcast.
""" """
#Sanity checks #Sanity checks
assert validate_address(destination)[0] or is_burn_destination(destination) assert validate_address(destination)[0] or is_burn_destination(destination)
@ -139,37 +145,53 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
tx = make_shuffled_tx(list(utxos.keys()), outs, 2, tx_locktime) tx = make_shuffled_tx(list(utxos.keys()), outs, 2, tx_locktime)
inscripts = {} inscripts = {}
spent_outs = []
for i, txinp in enumerate(tx.vin): for i, txinp in enumerate(tx.vin):
u = (txinp.prevout.hash[::-1], txinp.prevout.n) u = (txinp.prevout.hash[::-1], txinp.prevout.n)
inscripts[i] = (utxos[u]["script"], utxos[u]["value"]) inscripts[i] = (utxos[u]["script"], utxos[u]["value"])
success, msg = wallet_service.sign_tx(tx, inscripts) spent_outs.append(CMutableTxOut(utxos[u]["value"],
if not success: utxos[u]["script"]))
log.error("Failed to sign transaction, quitting. Error msg: " + msg) if with_final_psbt:
return # here we have the PSBTWalletMixin do the signing stage
log.info("Got signed transaction:\n") # for us:
log.info(pformat(str(tx))) new_psbt = wallet_service.create_psbt_from_tx(tx, spent_outs=spent_outs)
log.info("In serialized form (for copy-paste):") serialized_psbt, err = wallet_service.sign_psbt(new_psbt.serialize())
log.info(bintohex(tx.serialize())) if err:
actual_amount = amount if amount != 0 else total_inputs_val - fee_est log.error("Failed to sign PSBT, quitting. Error message: " + err)
log.info("Sends: " + amount_to_str(actual_amount) + " to destination: " + destination) return False
if not answeryes: new_psbt_signed = PartiallySignedTransaction.deserialize(serialized_psbt)
if not accept_callback: print("Completed PSBT created: ")
if input('Would you like to push to the network? (y/n):')[0] != 'y': print(pformat(new_psbt_signed))
log.info("You chose not to broadcast the transaction, quitting.") # TODO add more readable info here as for case below.
return False return new_psbt_signed
else: else:
accepted = accept_callback(pformat(str(tx)), destination, actual_amount, success, msg = wallet_service.sign_tx(tx, inscripts)
fee_est) if not success:
if not accepted: log.error("Failed to sign transaction, quitting. Error msg: " + msg)
return False return
jm_single().bc_interface.pushtx(tx.serialize()) log.info("Got signed transaction:\n")
txid = bintohex(tx.GetTxid()[::-1]) log.info(pformat(str(tx)))
successmsg = "Transaction sent: " + txid log.info("In serialized form (for copy-paste):")
cb = log.info if not info_callback else info_callback log.info(bintohex(tx.serialize()))
cb(successmsg) actual_amount = amount if amount != 0 else total_inputs_val - fee_est
txinfo = txid if not return_transaction else tx log.info("Sends: " + amount_to_str(actual_amount) + " to destination: " + destination)
return txinfo if not answeryes:
if not accept_callback:
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 False
else:
accepted = accept_callback(pformat(str(tx)), destination, actual_amount,
fee_est)
if not accepted:
return False
jm_single().bc_interface.pushtx(tx.serialize())
txid = bintohex(tx.GetTxid()[::-1])
successmsg = "Transaction sent: " + txid
cb = log.info if not info_callback else info_callback
cb(successmsg)
txinfo = txid if not return_transaction else tx
return txinfo
def sign_tx(wallet_service, tx, utxos): def sign_tx(wallet_service, tx, utxos):
stx = deserialize(tx) stx = deserialize(tx)

7
scripts/sendpayment.py

@ -190,7 +190,12 @@ def main():
.format(exp_tx_fees_ratio)) .format(exp_tx_fees_ratio))
if options.makercount == 0 and not options.p2ep: if options.makercount == 0 and not options.p2ep:
direct_send(wallet_service, amount, mixdepth, destaddr, options.answeryes) tx = direct_send(wallet_service, amount, mixdepth, destaddr,
options.answeryes, with_final_psbt=options.with_psbt)
if options.with_psbt:
log.info("This PSBT is fully signed and can be sent externally for "
"broadcasting:")
log.info(tx.to_base64())
return return
if wallet.get_txtype() == 'p2pkh': if wallet.get_txtype() == 'p2pkh':

Loading…
Cancel
Save