@ -80,12 +80,17 @@ class Bucket(NamedTuple):
def strip_unneeded ( bkts , sufficient_funds ) :
''' Remove buckets that are unnecessary in achieving the spend amount '''
bkts = sorted ( bkts , key = lambda bkt : bkt . value )
if sufficient_funds ( [ ] , bucket_value_sum = 0 ) :
# none of the buckets are needed
return [ ]
bkts = sorted ( bkts , key = lambda bkt : bkt . value , reverse = True )
bucket_value_sum = 0
for i in range ( len ( bkts ) ) :
if not sufficient_funds ( bkts [ i + 1 : ] ) :
return bkts [ i : ]
# none of the buckets are needed
return [ ]
bucket_value_sum + = ( bkts [ i ] ) . value
if sufficient_funds ( bkts [ : i + 1 ] , bucket_value_sum = bucket_value_sum ) :
return bkts [ : i + 1 ]
raise Exception ( " keeping all buckets is still not enough " )
class CoinChooserBase ( PrintError ) :
@ -230,10 +235,15 @@ class CoinChooserBase(PrintError):
return total_weight
def sufficient_funds ( buckets ) :
def sufficient_funds ( buckets , * , bucket_value_sum ) :
''' Given a list of buckets, return True if it has enough
value to pay for the transaction '''
total_input = input_value + sum ( bucket . value for bucket in buckets )
# assert bucket_value_sum == sum(bucket.value for bucket in buckets) # expensive!
total_input = input_value + bucket_value_sum
if total_input < spent_amount : # shortcut for performance
return False
# note re performance: so far this was constant time
# what follows is linear in len(buckets)
total_weight = get_tx_weight ( buckets )
return total_input > = spent_amount + fee_estimator_w ( total_weight )
@ -278,7 +288,7 @@ class CoinChooserRandom(CoinChooserBase):
# Add all singletons
for n , bucket in enumerate ( buckets ) :
if sufficient_funds ( [ bucket ] ) :
if sufficient_funds ( [ bucket ] , bucket_value_sum = bucket . value ) :
candidates . add ( ( n , ) )
# And now some random ones
@ -289,9 +299,12 @@ class CoinChooserRandom(CoinChooserBase):
# incrementally combine buckets until sufficient
self . p . shuffle ( permutation )
bkts = [ ]
bucket_value_sum = 0
for count , index in enumerate ( permutation ) :
bkts . append ( buckets [ index ] )
if sufficient_funds ( bkts ) :
bucket = buckets [ index ]
bkts . append ( bucket )
bucket_value_sum + = bucket . value
if sufficient_funds ( bkts , bucket_value_sum = bucket_value_sum ) :
candidates . add ( tuple ( sorted ( permutation [ : count + 1 ] ) ) )
break
else :
@ -320,16 +333,20 @@ class CoinChooserRandom(CoinChooserBase):
bucket_sets = [ conf_buckets , unconf_buckets , other_buckets ]
already_selected_buckets = [ ]
already_selected_buckets_value_sum = 0
for bkts_choose_from in bucket_sets :
try :
def sfunds ( bkts ) :
return sufficient_funds ( already_selected_buckets + bkts )
def sfunds ( bkts , * , bucket_value_sum ) :
bucket_value_sum + = already_selected_buckets_value_sum
return sufficient_funds ( already_selected_buckets + bkts ,
bucket_value_sum = bucket_value_sum )
candidates = self . bucket_candidates_any ( bkts_choose_from , sfunds )
break
except NotEnoughFunds :
already_selected_buckets + = bkts_choose_from
already_selected_buckets_value_sum + = sum ( bucket . value for bucket in bkts_choose_from )
else :
raise NotEnoughFunds ( )