|
|
|
@ -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) |
|
|
|
|