Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the command line option of "input-addrs" #1112

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions jmclient/jmclient/cli_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,12 @@ def get_sendpayment_parser():
dest='mixdepth',
help='mixing depth to spend from, default=0',
default=0)
parser.add_option('-i',
'--input-addrs',
action='append',
dest='input_addrs',
help='input addresses to spend from, must belong to the same mixing depth, default=[]',
default=[])
parser.add_option('-a',
'--amtmixdepths',
action='store',
Expand Down
12 changes: 10 additions & 2 deletions jmclient/jmclient/taker.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ def initialize(self, orderbook, fidelity_bonds_info):
self.mixdepth = si[0]
self.cjamount = si[1]
rounding = si[5]
self.input_addrs = None
if len(si) > 7:
self.input_addrs = si[7]

#non-integer coinjoin amounts are treated as fractions
#this is currently used by the tumbler algo
if isinstance(self.cjamount, float):
Expand Down Expand Up @@ -334,8 +338,12 @@ def prepare_my_bitcoin_data(self):
total_amount = self.cjamount + self.total_cj_fee + self.total_txfee
jlog.info('total estimated amount spent = ' + btc.amount_to_str(total_amount))
try:
self.input_utxos = self.wallet_service.select_utxos(self.mixdepth, total_amount,
minconfs=1)
if self.input_addrs:
self.input_utxos = self.wallet_service.select_utxos_from_addrs(
self.mixdepth, self.input_addrs, total_amount)
else:
self.input_utxos = self.wallet_service.select_utxos(self.mixdepth, total_amount,
minconfs=1)
except Exception as e:
self.taker_info_callback("ABORT",
"Unable to select sufficient coins: " + repr(e))
Expand Down
7 changes: 5 additions & 2 deletions jmclient/jmclient/taker_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
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=False, custom_change_addr=None):
optin_rbf=False, custom_change_addr=None, input_addrs=None):
"""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.
Expand Down Expand Up @@ -118,7 +118,10 @@ def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
#not doing a sweep; we will have change
#8 inputs to be conservative
initial_fee_est = estimate_tx_fee(8,2, txtype=txtype, outtype=outtype)
utxos = wallet_service.select_utxos(mixdepth, amount + initial_fee_est)
if input_addrs:
utxos = wallet_service.select_utxos_from_addrs(mixdepth, input_addrs, amount + initial_fee_est)
else:
utxos = wallet_service.select_utxos(mixdepth, amount + initial_fee_est)
if len(utxos) < 8:
fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype, outtype=outtype)
else:
Expand Down
42 changes: 42 additions & 0 deletions jmclient/jmclient/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,19 @@ def select_utxos(self, mixdepth, amount, utxo_filter=(), select_fn=None,
'value': utxos[s['utxo']][1]}
for s in selected}

def select_utxos_from_paths(self, mixdepth, input_paths, amount):
assert isinstance(mixdepth, numbers.Integral)
utxos = self._utxo[mixdepth]
# select everything from the input_paths list
available = [{'utxo': utxo, 'value': val}
for utxo, (addr, val, height) in utxos.items() if addr in input_paths]
# do not select anything disabled
available = [u for u in available if not self.is_disabled(*u['utxo'])]
selected = self.selector(available, amount)
return {s['utxo']: {'path': utxos[s['utxo']][0],
'value': utxos[s['utxo']][1]}
for s in selected}

def get_balance_by_mixdepth(self, max_mixdepth=float('Inf'),
include_disabled=True, maxheight=None):
""" By default this returns a dict of aggregated bitcoin
Expand Down Expand Up @@ -784,6 +797,35 @@ def select_utxos(self, mixdepth, amount, utxo_filter=None,

return utxos

def select_utxos_from_addrs(self, mixdepth, input_addrs, amount):
"""
Select a subset of available UTXOS from a given list of addresses whose values
added together is greater or equal to amount.

args:
mixdepth: int, mixdepth to select utxos from, must be smaller or
equal to wallet.max_mixdepth
input_addrs: list of strings, the addresses to select utxos from, must belong
to the mixdepth specified
amount: int, total minimum amount of all selected utxos

returns:
{(txid, index): {'script': bytes, 'path': tuple, 'value': int}}

raises:
NotEnoughFundsException: if mixdepth does not have utxos with
enough value to satisfy amount
"""
assert isinstance(amount, numbers.Integral)
input_paths = [self.addr_to_path(addr) for addr in input_addrs]
utxos = self._utxos.select_utxos_from_paths(
mixdepth, input_paths, amount)

for _, data in utxos.items():
data['script'] = self.get_script_from_path(data['path'])

return utxos

def disable_utxo(self, txid, index, disable=True):
self._utxos.disable_utxo(txid, index, disable)
# make sure the utxo database is persisted
Expand Down
6 changes: 6 additions & 0 deletions jmclient/jmclient/wallet_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,12 @@ def minconfs_to_maxheight(self, minconfs):
else:
return self.current_blockheight - minconfs + 1

def select_utxos_from_addrs(self, mixdepth, input_addrs, amount):
""" Request utxos from the specified addresses of the wallet to satisfy
a certain total amount.
"""
return self.wallet.select_utxos_from_addrs(mixdepth, input_addrs, amount)

def select_utxos(self, mixdepth, amount, utxo_filter=None, select_fn=None,
minconfs=None, includeaddr=False, require_auth_address=False):
""" Request utxos from the wallet in a particular mixdepth to satisfy
Expand Down
5 changes: 3 additions & 2 deletions scripts/sendpayment.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def main():
"error")
sys.exit(EXIT_ARGERROR)
schedule = [[options.mixdepth, amount, options.makercount,
destaddr, 0.0, NO_ROUNDING, 0]]
destaddr, 0.0, NO_ROUNDING, 0, options.input_addrs]]
else:
if len(args) > 1:
parser.error("Schedule files are not compatible with "
Expand Down Expand Up @@ -244,7 +244,8 @@ 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,
optin_rbf=options.rbf, custom_change_addr=custom_change)
optin_rbf=options.rbf, custom_change_addr=custom_change,
input_addrs=options.input_addrs)
if options.with_psbt:
log.info("This PSBT is fully signed and can be sent externally for "
"broadcasting:")
Expand Down