URI: 
       tRemove unneeded buckets for Privacy coin chooser - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit ea49e8dc968cf41af98400208d3b7122492bec09
   DIR parent f3a7d3f2bff502e26e53cb63a1175da86d3bb08d
  HTML Author: Neil Booth <kyuupichan@gmail.com>
       Date:   Sat, 12 Dec 2015 10:12:46 +0900
       
       Remove unneeded buckets for Privacy coin chooser
       
       Commonize the code with the classic chooser and simplify.
       
       Diffstat:
         M lib/coinchooser.py                  |      57 ++++++++++++++-----------------
       
       1 file changed, 26 insertions(+), 31 deletions(-)
       ---
   DIR diff --git a/lib/coinchooser.py b/lib/coinchooser.py
       t@@ -25,6 +25,15 @@ from util import NotEnoughFunds, PrintError, profiler
        
        Bucket = namedtuple('Bucket', ['desc', 'size', 'value', 'coins'])
        
       +def strip_unneeded(bkts, sufficient_funds):
       +    '''Remove buckets that are unnecessary in achieving the spend amount'''
       +    bkts = sorted(bkts, key = lambda bkt: bkt.value)
       +    for i in range(len(bkts)):
       +        if not sufficient_funds(bkts[i + 1:]):
       +            return bkts[i:]
       +    # Shouldn't get here
       +    return bkts
       +
        class CoinChooserBase(PrintError):
        
            def keys(self, coins):
       t@@ -72,12 +81,18 @@ class CoinChooserBase(PrintError):
                tx = Transaction.from_io([], outputs[:])
                # Size of the transaction with no inputs and no change
                base_size = tx.estimated_size()
       -        # Returns fee given input size
       -        fee = lambda input_size: fee_estimator(base_size + input_size)
       +        spent_amount = tx.output_value()
       +
       +        def sufficient_funds(buckets):
       +            '''Given a list of buckets, return True if it has enough
       +            value to pay for the transaction'''
       +            total_input = sum(bucket.value for bucket in buckets)
       +            total_size = sum(bucket.size for bucket in buckets) + base_size
       +            return total_input >= spent_amount + fee_estimator(total_size)
        
                # Collect the coins into buckets, choose a subset of the buckets
                buckets = self.bucketize_coins(coins)
       -        buckets = self.choose_buckets(buckets, tx.output_value(), fee,
       +        buckets = self.choose_buckets(buckets, sufficient_funds,
                                              self.penalty_func(tx))
        
                tx.inputs = [coin for b in buckets for coin in b.coins]
       t@@ -103,33 +118,20 @@ class CoinChooserClassic(CoinChooserBase):
                return [coin['prevout_hash'] + ':' + str(coin['prevout_n'])
                        for coin in coins]
        
       -    def choose_buckets(self, buckets, spent_amount, fee, penalty_func):
       +    def choose_buckets(self, buckets, sufficient_funds, penalty_func):
                '''Spend the oldest buckets first.'''
                # Unconfirmed coins are young, not old
                adj_height = lambda height: 99999999 if height == 0 else height
                buckets.sort(key = lambda b: max(adj_height(coin['height'])
                                                 for coin in b.coins))
       -        selected, value, size = [], 0, 0
       +        selected = []
                for bucket in buckets:
                    selected.append(bucket)
       -            value += bucket.value
       -            size += bucket.size
       -            if value >= spent_amount + fee(size):
       -                break
       +            if sufficient_funds(selected):
       +                return strip_unneeded(selected, sufficient_funds)
                else:
                    raise NotEnoughFunds()
        
       -        # Remove unneeded inputs starting with the smallest.
       -        selected.sort(key = lambda b: b.value)
       -        dropped = []
       -        for bucket in selected:
       -            if value - bucket.value >= spent_amount + fee(size - bucket.size):
       -                value -= bucket.value
       -                size -= bucket.size
       -                dropped.append(bucket)
       -
       -        return [bucket for bucket in selected if bucket not in dropped]
       -
        class CoinChooserRandom(CoinChooserBase):
        
            def bucket_candidates(self, buckets, sufficient_funds):
       t@@ -157,18 +159,11 @@ class CoinChooserRandom(CoinChooserBase):
                    else:
                        raise NotEnoughFunds()
        
       -        return [[buckets[n] for n in candidate] for candidate in candidates]
       -
       -    def choose_buckets(self, buckets, spent_amount, fee, penalty_func):
       -
       -        def sufficient(buckets):
       -            '''Given a set of buckets, return True if it has enough
       -            value to pay for the transaction'''
       -            total_input = sum(bucket.value for bucket in buckets)
       -            total_size = sum(bucket.size for bucket in buckets)
       -            return total_input >= spent_amount + fee(total_size)
       +        candidates = [[buckets[n] for n in c] for c in candidates]
       +        return [strip_unneeded(c, sufficient_funds) for c in candidates]
        
       -        candidates = self.bucket_candidates(buckets, sufficient)
       +    def choose_buckets(self, buckets, sufficient_funds, penalty_func):
       +        candidates = self.bucket_candidates(buckets, sufficient_funds)
                penalties = [penalty_func(cand) for cand in candidates]
                winner = candidates[penalties.index(min(penalties))]
                self.print_error("Bucket sets:", len(buckets))