URI: 
       tMerge pull request #916 from Tafelpoot/optimize - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e3cfc31abbd064ccf9f1b3bfe345cf88845e7d1a
   DIR parent 5a30069d227a634d71a235901936f5ebf5710113
  HTML Author: ThomasV <thomasv1@gmx.de>
       Date:   Fri,  7 Nov 2014 17:24:44 +0100
       
       Merge pull request #916 from Tafelpoot/optimize
       
       Optimized transaction.py a bit more
       Diffstat:
         M lib/transaction.py                  |      97 +++++++++++++------------------
         M lib/wallet.py                       |      56 +++++++++++++++++--------------
       
       2 files changed, 73 insertions(+), 80 deletions(-)
       ---
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -386,15 +386,13 @@ def parse_scriptSig(d, bytes):
                return
        
            # p2sh transaction, 2 of n
       -    match = [ opcodes.OP_0 ]
       -    while len(match) < len(decoded):
       -        match.append(opcodes.OP_PUSHDATA4)
       +    match = [ opcodes.OP_0 ] + [ opcodes.OP_PUSHDATA4 ] * (len(decoded) - 1)
        
            if not match_decoded(decoded, match):
                print_error("cannot find address in input script", bytes.encode('hex'))
                return
        
       -    x_sig = map(lambda x:x[1].encode('hex'), decoded[1:-1])
       +    x_sig = [x[1].encode('hex') for x in decoded[1:-1]]
            d['signatures'] = parse_sig(x_sig)
            d['num_sig'] = 2
        
       t@@ -410,7 +408,7 @@ def parse_scriptSig(d, bytes):
                return
        
            d['x_pubkeys'] = x_pubkeys
       -    pubkeys = map(lambda x: parse_xpub(x)[0], x_pubkeys)
       +    pubkeys = [parse_xpub(x)[0] for x in x_pubkeys]     # xpub, addr = parse_xpub()
            d['pubkeys'] = pubkeys
            redeemScript = Transaction.multisig_script(pubkeys,2)
            d['redeemScript'] = redeemScript
       t@@ -495,7 +493,9 @@ def deserialize(raw):
            return d
        
        
       -push_script = lambda x: op_push(len(x)/2) + x
       +def push_script(x):
       +    return op_push(len(x)/2) + x
       +
        
        class Transaction:
        
       t@@ -520,7 +520,7 @@ class Transaction:
                d = deserialize(raw)
                self.raw = raw
                self.inputs = d['inputs']
       -        self.outputs = map(lambda x: (x['type'], x['address'], x['value']), d['outputs'])
       +        self.outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
                self.locktime = d['lockTime']
        
            @classmethod
       t@@ -546,7 +546,7 @@ class Transaction:
                if not inputs:
                    return
        
       -        total = sum( map(lambda x:int(x.get('value')), inputs) ) - fee
       +        total = sum(i.get('value') for i in inputs) - fee
                outputs = [('address', to_address, total)]
                self = klass(inputs, outputs)
                self.sign({ pubkey:privkey })
       t@@ -567,8 +567,7 @@ class Transaction:
                    raise
        
                for k in public_keys:
       -            s += op_push(len(k)/2)
       -            s += k
       +            s += op_push(len(k)/2) + k
                if n==2:
                    s += '52'
                elif n==3:
       t@@ -612,8 +611,7 @@ class Transaction:
        
                s  = int_to_hex(1,4)                                         # version
                s += var_int( len(inputs) )                                  # number of inputs
       -        for i in range(len(inputs)):
       -            txin = inputs[i]
       +        for i, txin in enumerate(inputs):
        
                    s += txin['prevout_hash'].decode('hex')[::-1].encode('hex')   # prev hash
                    s += int_to_hex(txin['prevout_n'],4)                          # prev index
       t@@ -623,37 +621,31 @@ class Transaction:
                    address = txin['address']
        
                    x_signatures = txin['signatures']
       -            signatures = filter(lambda x: x is not None, x_signatures)
       +            signatures = filter(None, x_signatures)
                    is_complete = len(signatures) == num_sig
        
                    if for_sig in [-1, None]:
                        # if we have enough signatures, we use the actual pubkeys
                        # use extended pubkeys (with bip32 derivation)
       -                sig_list = []
                        if for_sig == -1:
                            # we assume that signature will be 0x48 bytes long
                            pubkeys = txin['pubkeys']
       -                    sig_list = [ "00"* 0x48 ] * num_sig
       +                    sig_list = [ "00" * 0x48 ] * num_sig
                        elif is_complete:
                            pubkeys = txin['pubkeys']
       -                    for signature in signatures:
       -                        sig_list.append(signature + '01')
       +                    sig_list = ((sig + '01') for sig in signatures)
                        else:
                            pubkeys = txin['x_pubkeys']
       -                    for signature in x_signatures:
       -                        sig_list.append((signature + '01') if signature is not None else NO_SIGNATURE)
       -
       -                sig_list = ''.join( map( lambda x: push_script(x), sig_list))
       +                    sig_list = ((sig + '01') if sig else NO_SIGNATURE for sig in x_signatures)
       +                script = ''.join(push_script(x) for x in sig_list)
                        if not p2sh:
       -                    script = sig_list
                            x_pubkey = pubkeys[0]
                            if x_pubkey is None:
                                addrtype, h160 = bc_address_to_hash_160(txin['address'])
                                x_pubkey = 'fd' + (chr(addrtype) + h160).encode('hex')
                            script += push_script(x_pubkey)
                        else:
       -                    script = '00'                                    # op_0
       -                    script += sig_list
       +                    script = '00' + script          # put op_0 in front of script
                            redeem_script = self.multisig_script(pubkeys,2)
                            script += push_script(redeem_script)
        
       t@@ -689,10 +681,10 @@ class Transaction:
                self.raw = None
        
            def input_value(self):
       -        return sum([x['value'] for x in self.inputs])
       +        return sum(x['value'] for x in self.inputs)
        
            def output_value(self):
       -        return sum([ x[2] for x in self.outputs])
       +        return sum( val for tp,addr,val in self.outputs)
        
            def get_fee(self):
                return self.input_value() - self.output_value()
       t@@ -703,7 +695,7 @@ class Transaction:
                for txin in self.inputs:
                    if txin.get('is_coinbase'):
                        continue
       -            signatures = filter(lambda x: x is not None, txin['signatures'])
       +            signatures = filter(None, txin['signatures'])
                    s += len(signatures)
                    r += txin['num_sig']
                return s, r
       t@@ -716,7 +708,7 @@ class Transaction:
                out = set()
                for txin in self.inputs:
                    x_signatures = txin['signatures']
       -            signatures = filter(lambda x: x is not None, x_signatures)
       +            signatures = filter(None, x_signatures)
                    if len(signatures) == txin['num_sig']:
                        # input is complete
                        continue
       t@@ -730,7 +722,7 @@ class Transaction:
            def sign(self, keypairs):
                print_error("tx.sign(), keypairs:", keypairs)
                for i, txin in enumerate(self.inputs):
       -            signatures = filter(lambda x: x is not None, txin['signatures'])
       +            signatures = filter(None, txin['signatures'])
                    num = txin['num_sig']
                    if len(signatures) == num:
                        # continue if this txin is complete
       t@@ -763,14 +755,14 @@ class Transaction:
                self.raw = self.serialize()
        
        
       -    def add_pubkey_addresses(self, txlist):
       -        for i in self.inputs:
       -            if i.get("address") == "(pubkey)":
       -                prev_tx = txlist.get(i.get('prevout_hash'))
       +    def add_pubkey_addresses(self, txdict):
       +        for txin in self.inputs:
       +            if txin.get('address') == "(pubkey)":
       +                prev_tx = txdict.get(txin.get('prevout_hash'))
                        if prev_tx:
       -                    address, value = prev_tx.get_outputs()[i.get('prevout_n')]
       +                    address, value = prev_tx.get_outputs()[txin.get('prevout_n')]
                            print_error("found pay-to-pubkey address:", address)
       -                    i["address"] = address
       +                    txin["address"] = address
        
        
            def get_outputs(self):
       t@@ -788,23 +780,15 @@ class Transaction:
                            addr = 'OP_RETURN: "' + x.encode('hex') + '"'
                    else:
                        addr = "(None)"
       -            o.append((addr,v))
       +            o.append((addr,v))      # consider using yield (addr, v)
                return o
        
            def get_output_addresses(self):
       -        return map(lambda x:x[0], self.get_outputs())
       +        return [addr for addr, val in self.get_outputs()]
        
        
            def has_address(self, addr):
       -        found = False
       -        for txin in self.inputs:
       -            if addr == txin.get('address'):
       -                found = True
       -                break
       -        if addr in self.get_output_addresses():
       -            found = True
       -
       -        return found
       +        return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs))
        
        
            def get_value(self, addresses, prevout_values):
       t@@ -871,19 +855,22 @@ class Transaction:
        
            def requires_fee(self, verifier):
                # see https://en.bitcoin.it/wiki/Transaction_fees
       -        threshold = 57600000
       +        #
       +        # size must be smaller than 1 kbyte for free tx
                size = len(self.serialize(-1))/2
                if size >= 10000:
                    return True
       -
       -        for o in self.get_outputs():
       -            value = o[1]
       +        # all outputs must be 0.01 BTC or larger for free tx
       +        for addr, value in self.get_outputs():
                    if value < 1000000:
                        return True
       -        sum = 0
       -        for i in self.inputs:
       -            age = verifier.get_confirmations(i["prevout_hash"])[0]
       -            sum += i["value"] * age
       -        priority = sum / size
       +        # priority must be large enough for free tx
       +        threshold = 57600000
       +        weight = 0
       +        for txin in self.inputs:
       +            age = verifier.get_confirmations(txin["prevout_hash"])[0]
       +            weight += txin["value"] * age
       +        priority = weight / size
                print_error(priority, threshold)
       +
                return priority < threshold
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -329,10 +329,7 @@ class Abstract_Wallet(object):
                return changed
        
            def addresses(self, include_change = True):
       -        o = []
       -        for a in self.accounts.keys():
       -            o += self.get_account_addresses(a, include_change)
       -        return o
       +        return list(addr for acc in self.accounts for addr in self.get_account_addresses(acc, include_change))
        
            def is_mine(self, address):
                return address in self.addresses(True)
       t@@ -344,12 +341,11 @@ class Abstract_Wallet(object):
                return s[0] == 1
        
            def get_address_index(self, address):
       -        for account in self.accounts.keys():
       +        for acc_id in self.accounts:
                    for for_change in [0,1]:
       -                addresses = self.accounts[account].get_addresses(for_change)
       -                for addr in addresses:
       -                    if address == addr:
       -                        return account, (for_change, addresses.index(addr))
       +                addresses = self.accounts[acc_id].get_addresses(for_change)
       +                if address in addresses:
       +                    return acc_id, (for_change, addresses.index(address))
                raise Exception("Address not found", address)
        
            def get_private_key(self, address, password):
       t@@ -394,7 +390,8 @@ class Abstract_Wallet(object):
                    self.storage.put('addressbook', self.addressbook, True)
        
            def fill_addressbook(self):
       -        for tx_hash, tx in self.transactions.items():
       +        # todo: optimize this
       +        for tx_hash, tx in self.transactions.viewitems():
                    is_relevant, is_send, _, _ = self.get_tx_value(tx)
                    if is_send:
                        for addr in tx.get_output_addresses():
       t@@ -426,12 +423,13 @@ class Abstract_Wallet(object):
                        self.spent_outputs.append(key)
        
            def get_addr_balance(self, address):
       +        'returns the confirmed balance and pending (unconfirmed) balance change of this bitcoin address'
                #assert self.is_mine(address)
                h = self.history.get(address,[])
                if h == ['*']: return 0,0
                c = u = 0
                received_coins = []   # list of coins received at address
       -
       +        # go through all tx in history of this address and collect the coins arriving on this address
                for tx_hash, tx_height in h:
                    tx = self.transactions.get(tx_hash)
                    if not tx: continue
       t@@ -440,12 +438,12 @@ class Abstract_Wallet(object):
                        if addr == address:
                            key = tx_hash + ':%d'%i
                            received_coins.append(key)
       -
       +        # go through all tx in history of this address again
                for tx_hash, tx_height in h:
                    tx = self.transactions.get(tx_hash)
                    if not tx: continue
                    v = 0
       -
       +            # substract the value of coins leaving from this address
                    for item in tx.inputs:
                        addr = item.get('address')
                        if addr == address:
       t@@ -453,16 +451,16 @@ class Abstract_Wallet(object):
                            value = self.prevout_values.get( key )
                            if key in received_coins:
                                v -= value
       -
       +            # add the value of the coins arriving in this address
                    for i, (addr, value) in enumerate(tx.get_outputs()):
                        key = tx_hash + ':%d'%i
                        if addr == address:
                            v += value
        
                    if tx_height:
       -                c += v
       +                c += v  # confirmed coins value
                    else:
       -                u += v
       +                u += v  # unconfirmed coins value
                return c, u
        
            def get_account_name(self, k):
       t@@ -474,14 +472,22 @@ class Abstract_Wallet(object):
                    account_names[k] = self.get_account_name(k)
                return account_names
        
       -    def get_account_addresses(self, a, include_change=True):
       -        if a is None:
       -            o = self.addresses(include_change)
       -        elif a in self.accounts:
       -            ac = self.accounts[a]
       -            o = ac.get_addresses(0)
       -            if include_change: o += ac.get_addresses(1)
       -        return o
       +    def get_account_addresses(self, acc_id, include_change=True):
       +        if acc_id is None:
       +            addr_list = self.addresses(include_change)
       +        elif acc_id in self.accounts:
       +            acc = self.accounts[acc_id]
       +            addr_list = acc.get_addresses(0)
       +            if include_change:
       +                addr_list += acc.get_addresses(1)
       +        return addr_list
       +
       +    def get_account_from_address(self, addr):
       +        "Returns the account that contains this address, or None"
       +        for acc_id in self.accounts:    # similar to get_address_index but simpler
       +            if addr in self.get_account_addresses(acc_id):
       +                return self.accounts[acc_id]
       +        return None
        
            def get_account_balance(self, account):
                return self.get_balance(self.get_account_addresses(account))
       t@@ -524,7 +530,7 @@ class Abstract_Wallet(object):
                    if coins[-1][0] != 0:
                        while coins[0][0] == 0:
                            coins = coins[1:] + [ coins[0] ]
       -        return [x[1] for x in coins]
       +        return [value for height, value in coins]