diff --git a/lib/transaction.py b/lib/transaction.py index 4bc391591..09568f92c 100644 --- a/lib/transaction.py +++ b/lib/transaction.py @@ -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 @@ -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 @@ -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: @@ -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 @@ -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 }) @@ -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: @@ -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 @@ -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) @@ -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() @@ -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 @@ -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 @@ -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 @@ -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): @@ -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): @@ -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 diff --git a/lib/wallet.py b/lib/wallet.py index cef94f290..7d8ed60df 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -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) @@ -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): @@ -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(): @@ -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 @@ -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: @@ -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): @@ -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)) @@ -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]