URI: 
       tMerge branch 'master' of git://github.com/spesmilo/electrum - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 9a98ad2881f67dab338e5a01c5d85169be2ef76e
   DIR parent 5be9f03fdfa12788e9650d0da809cbfbb2d31061
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Sat, 16 Jan 2016 12:01:49 +0100
       
       Merge branch 'master' of git://github.com/spesmilo/electrum
       
       Diffstat:
         M lib/coinchooser.py                  |      52 ++++++++++++++++++++++++++++---
         M lib/wallet.py                       |      11 -----------
       
       2 files changed, 47 insertions(+), 16 deletions(-)
       ---
   DIR diff --git a/lib/coinchooser.py b/lib/coinchooser.py
       t@@ -17,13 +17,51 @@
        # along with this program. If not, see <http://www.gnu.org/licenses/>.
        
        from collections import defaultdict, namedtuple
       -from random import choice, randint, shuffle
        from math import floor, log10
       +import struct
        
       -from bitcoin import COIN, TYPE_ADDRESS
       +from bitcoin import sha256, COIN, TYPE_ADDRESS
        from transaction import Transaction
        from util import NotEnoughFunds, PrintError, profiler
        
       +# A simple deterministic PRNG.  Used to deterministically shuffle a
       +# set of coins - the same set of coins should produce the same output.
       +# Although choosing UTXOs "randomly" we want it to be deterministic,
       +# so if sending twice from the same UTXO set we choose the same UTXOs
       +# to spend.  This prevents attacks on users by malicious or stale
       +# servers.
       +
       +class PRNG:
       +    def __init__(self, seed):
       +        self.sha = sha256(seed)
       +        self.pool = bytearray()
       +
       +    def get_bytes(self, n):
       +        while len(self.pool) < n:
       +            self.pool.extend(self.sha)
       +            self.sha = sha256(self.sha)
       +        result, self.pool = self.pool[:n], self.pool[n:]
       +        return result
       +
       +    def random(self):
       +        # Returns random double in [0, 1)
       +        four = self.get_bytes(4)
       +        return struct.unpack("I", four)[0] / 4294967296.0
       +
       +    def randint(self, start, end):
       +        # Returns random integer in [start, end)
       +        return start + int(self.random() * (end - start))
       +
       +    def choice(self, seq):
       +        return seq[int(self.random() * len(seq))]
       +
       +    def shuffle(self, x):
       +        for i in reversed(xrange(1, len(x))):
       +            # pick an element in x[:i+1] with which to exchange x[i]
       +            j = int(self.random() * (i+1))
       +            x[i], x[j] = x[j], x[i]
       +
       +
        Bucket = namedtuple('Bucket', ['desc', 'size', 'value', 'coins'])
        
        def strip_unneeded(bkts, sufficient_funds):
       t@@ -87,8 +125,8 @@ class CoinChooserBase(PrintError):
                amounts = []
                while n > 1:
                    average = remaining // n
       -            amount = randint(int(average * 0.7), int(average * 1.3))
       -            precision = min(choice(zeroes), int(floor(log10(amount))))
       +            amount = self.p.randint(int(average * 0.7), int(average * 1.3))
       +            precision = min(self.p.choice(zeroes), int(floor(log10(amount))))
                    amount = int(round(amount, -precision))
                    amounts.append(amount)
                    remaining -= amount
       t@@ -127,6 +165,10 @@ class CoinChooserBase(PrintError):
                the transaction) it is kept, otherwise none is sent and it is
                added to the transaction fee.'''
        
       +        # Deterministic randomness from coins
       +        utxos = [c['prevout_hash'] + str(c['prevout_n']) for c in coins]
       +        self.p = PRNG(''.join(sorted(utxos)))
       +
                # Copy the ouputs so when adding change we don't modify "outputs"
                tx = Transaction.from_io([], outputs[:])
                # Size of the transaction with no inputs and no change
       t@@ -201,7 +243,7 @@ class CoinChooserRandom(CoinChooserBase):
                for i in range(attempts):
                    # Get a random permutation of the buckets, and
                    # incrementally combine buckets until sufficient
       -            shuffle(permutation)
       +            self.p.shuffle(permutation)
                    bkts = []
                    for count, index in enumerate(permutation):
                        bkts.append(buckets[index])
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -196,7 +196,6 @@ class Abstract_Wallet(PrintError):
                self.lock = threading.Lock()
                self.transaction_lock = threading.Lock()
                self.tx_event = threading.Event()
       -        self.tx_cache = (None, None, None, None, None)
        
                self.check_history()
        
       t@@ -966,14 +965,6 @@ class Abstract_Wallet(PrintError):
                # Change <= dust threshold is added to the tx fee
                dust_threshold = 182 * 3 * self.relayfee() / 1000
        
       -        # Check cache to see if we just calculated this.  If prior
       -        # calculated a fee and this fixes it to the same, return same
       -        # answer, to avoid random coin selection changing the answer
       -        if self.tx_cache[:4] == (outputs, coins, change_addrs, dust_threshold):
       -            tx = self.tx_cache[4]
       -            if tx.get_fee() == fee_estimator(tx.estimated_size()):
       -                return tx
       -
                # Let the coin chooser select the coins to spend
                max_change = 3 if self.multiple_change else 1
                coin_chooser = self.coin_chooser(config)
       t@@ -983,8 +974,6 @@ class Abstract_Wallet(PrintError):
                # Sort the inputs and outputs deterministically
                tx.BIP_LI01_sort()
        
       -        self.tx_cache = (outputs, coins, change_addrs, dust_threshold, tx)
       -
                run_hook('make_unsigned_transaction', self, tx)
                return tx