diff --git a/jmclient/jmclient/cli_options.py b/jmclient/jmclient/cli_options.py index 83b55b70c..1ec51125f 100644 --- a/jmclient/jmclient/cli_options.py +++ b/jmclient/jmclient/cli_options.py @@ -501,6 +501,12 @@ def get_sendpayment_parser(): default='', help='specify address to receive change ' ', by default use in-wallet address.') + parser.add_option('-l', + '--label-change', + type='str', + dest='changelabel', + default='', + help='specify address label for change output') add_common_options(parser) return parser diff --git a/jmclient/jmclient/taker.py b/jmclient/jmclient/taker.py index 228bd33df..6a58af3de 100644 --- a/jmclient/jmclient/taker.py +++ b/jmclient/jmclient/taker.py @@ -3,7 +3,7 @@ import base64 import pprint import random -from typing import Any, NamedTuple +from typing import Any, NamedTuple, Optional from twisted.internet import reactor, task import jmbitcoin as btc @@ -11,7 +11,8 @@ from jmbase import get_log, bintohex, hexbin from jmclient.support import (calc_cj_fee, fidelity_bond_weighted_order_choose, choose_orders, choose_sweep_orders) -from jmclient.wallet import estimate_tx_fee, compute_tx_locktime, FidelityBondMixin +from jmclient.wallet import (estimate_tx_fee, compute_tx_locktime, + FidelityBondMixin, UnknownAddressForLabel) from jmclient.podle import generate_podle, get_podle_commitments from jmclient.wallet_service import WalletService from jmclient.fidelity_bond import FidelityBondProof @@ -40,8 +41,8 @@ class _MakerTxData(NamedTuple): change_amount: Any real_cjfee: Any utxo_list: Any = None - cj_addr: Any = None - change_addr: Any = None + cj_addr: Optional[str] = None + change_addr: Optional[str] = None def __init__(self, wallet_service, @@ -51,6 +52,7 @@ def __init__(self, callbacks=None, tdestaddrs=None, custom_change_address=None, + change_label=None, ignored_makers=None): """`schedule`` must be a list of tuples: (see sample_schedule_for_testnet for explanation of syntax, also schedule.py module in this directory), @@ -103,6 +105,7 @@ def __init__(self, self.order_chooser = order_chooser self.max_cj_fee = max_cj_fee self.custom_change_address = custom_change_address + self.change_label = change_label #List (which persists between transactions) of makers #who have not responded or behaved maliciously at any @@ -319,6 +322,13 @@ def prepare_my_bitcoin_data(self): else: try: self.my_change_addr = self.wallet_service.get_internal_addr(self.mixdepth) + if self.change_label: + try: + self.wallet_service.set_address_label( + self.my_change_addr, self.change_label) + except UnknownAddressForLabel: + # ignore, will happen with custom change not part of a wallet + pass except: self.taker_info_callback("ABORT", "Failed to get a change address") return False diff --git a/jmclient/jmclient/taker_utils.py b/jmclient/jmclient/taker_utils.py index 7fb419351..e6fc30ac2 100644 --- a/jmclient/jmclient/taker_utils.py +++ b/jmclient/jmclient/taker_utils.py @@ -4,12 +4,15 @@ import sys import time import numbers +from typing import Callable, Optional, Union + from jmbase import get_log, jmprint, bintohex, hextobin from .configure import jm_single, validate_address, is_burn_destination from .schedule import human_readable_schedule_entry, tweak_tumble_schedule,\ schedule_to_text from .wallet import BaseWallet, estimate_tx_fee, compute_tx_locktime, \ - FidelityBondMixin + FidelityBondMixin, UnknownAddressForLabel +from .wallet_service import WalletService from jmbitcoin import make_shuffled_tx, amount_to_str, \ PartiallySignedTransaction, CMutableTxOut,\ human_readable_transaction @@ -30,10 +33,16 @@ def get_utxo_scripts(wallet: BaseWallet, utxos: dict) -> list: script_types.append(wallet.get_outtype(utxo["address"])) return script_types -def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False, - accept_callback=None, info_callback=None, error_callback=None, - return_transaction=False, with_final_psbt=False, - optin_rbf=True, custom_change_addr=None): +def direct_send(wallet_service: WalletService, amount: int, mixdepth: int, + destination: str, answeryes: bool = False, + accept_callback: Optional[Callable[[str, str, int, int, Optional[str]], bool]] = None, + info_callback: Optional[Callable[[str], None]] = None, + error_callback: Optional[Callable[[str], None]] = None, + return_transaction: bool = False, + with_final_psbt: bool = False, + optin_rbf: bool = True, + custom_change_addr: Optional[str] = None, + change_label: Optional[str] = None) -> Union[bool, str]: """Send coins directly from one mixdepth to one destination address; does not need IRC. Sweep as for normal sendpayment (set amount=0). If answeryes is True, callback/command line query is not performed. @@ -211,6 +220,12 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False, custom_change_addr) if not accepted: return False + if change_label: + try: + wallet_service.set_address_label(change_addr, change_label) + except UnknownAddressForLabel: + # ignore, will happen with custom change not part of a wallet + pass if jm_single().bc_interface.pushtx(tx.serialize()): txid = bintohex(tx.GetTxid()[::-1]) successmsg = "Transaction sent: " + txid diff --git a/scripts/sendpayment.py b/scripts/sendpayment.py index 9f2d8578e..802bd4b66 100755 --- a/scripts/sendpayment.py +++ b/scripts/sendpayment.py @@ -250,9 +250,11 @@ def main(): if options.makercount == 0 and not bip78url: tx = direct_send(wallet_service, amount, mixdepth, destaddr, - options.answeryes, with_final_psbt=options.with_psbt, + options.answeryes, + with_final_psbt=options.with_psbt, optin_rbf=not options.no_rbf, - custom_change_addr=custom_change) + custom_change_addr=custom_change, + change_label=options.changelabel) if options.with_psbt: log.info("This PSBT is fully signed and can be sent externally for " "broadcasting:") @@ -364,7 +366,8 @@ def taker_finished(res, fromtx=False, waittime=0.0, txdetails=None): order_chooser=chooseOrdersFunc, max_cj_fee=maxcjfee, callbacks=(filter_orders_callback, None, taker_finished), - custom_change_address=custom_change) + custom_change_address=custom_change, + change_label=options.changelabel) clientfactory = JMClientProtocolFactory(taker) if jm_single().config.get("BLOCKCHAIN", "network") == "regtest":