tfix createrawtransaction, using extended serialization format - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 52e9c0b4988e928a95623834c826d1145a52b422 DIR parent 3d32bba0b9570916c3a433d5c3737079b8f56c3f HTML Author: ThomasV <thomasv@gitorious> Date: Mon, 29 Dec 2014 20:26:00 +0100 fix createrawtransaction, using extended serialization format Diffstat: M lib/commands.py | 19 ++++++++++++++----- M lib/transaction.py | 122 +++++++++++++------------------ 2 files changed, 66 insertions(+), 75 deletions(-) --- DIR diff --git a/lib/commands.py b/lib/commands.py t@@ -65,7 +65,7 @@ verifymessage_syntax = 'verifymessage <address> <signature> <message>\nIf you wa register_command('contacts', 0, 0, False, True, False, 'Show your list of contacts') register_command('create', 0, 0, False, True, False, 'Create a new wallet') register_command('createmultisig', 2, 2, False, True, False, 'similar to bitcoind\'s command') -register_command('createrawtransaction', 2, 2, False, True, False, 'similar to bitcoind\'s command') +register_command('createrawtransaction', 2, 2, False, True, False, 'Create an unsigned transaction. The syntax is similar to bitcoind.') register_command('deseed', 0, 0, False, True, False, 'Remove seed from wallet, creating a seedless, watching-only wallet.') register_command('decoderawtransaction', 1, 1, False, False, False, 'similar to bitcoind\'s command') register_command('getprivatekeys', 1, 1, False, True, True, 'Get the private keys of a given address', 'getprivatekeys <bitcoin address>') t@@ -158,11 +158,20 @@ class Commands: return {'address':r[0] } def createrawtransaction(self, inputs, outputs): + coins = self.wallet.get_unspent_coins(None) + tx_inputs = [] for i in inputs: - i['prevout_hash'] = i['txid'] - i['prevout_n'] = i['vout'] - outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items()) - tx = Transaction(inputs, outputs) + prevout_hash = i['txid'] + prevout_n = i['vout'] + for c in coins: + if c['prevout_hash'] == prevout_hash and c['prevout_n'] == prevout_n: + self.wallet.add_input_info(c) + tx_inputs.append(c) + break + else: + raise BaseException('Transaction output not in wallet', prevout_hash+":%d"%prevout_n) + outputs = map(lambda x: ('address', x[0], int(1e8*x[1])), outputs.items()) + tx = Transaction(tx_inputs, outputs) return tx def signtxwithkey(self, raw_tx, sec): DIR diff --git a/lib/transaction.py b/lib/transaction.py t@@ -204,24 +204,6 @@ def short_hex(bytes): - -def parse_redeemScript(bytes): - dec = [ x for x in script_GetOp(bytes.decode('hex')) ] - - # 2 of 2 - match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_2, opcodes.OP_CHECKMULTISIG ] - if match_decoded(dec, match): - pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex') ] - return 2, pubkeys - - # 2 of 3 - match = [ opcodes.OP_2, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4, opcodes.OP_3, opcodes.OP_CHECKMULTISIG ] - if match_decoded(dec, match): - pubkeys = [ dec[1][1].encode('hex'), dec[2][1].encode('hex'), dec[3][1].encode('hex') ] - return 2, pubkeys - - - opcodes = Enumeration("Opcodes", [ ("OP_0", 0), ("OP_PUSHDATA1",76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED", "OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7", t@@ -581,12 +563,12 @@ class Transaction: @classmethod - def pay_script(self, type, addr): - if type == 'op_return': + def pay_script(self, output_type, addr): + if output_type == 'op_return': h = addr.encode('hex') return '6a' + push_script(h) else: - assert type == 'address' + assert output_type == 'address' addrtype, hash_160 = bc_address_to_hash_160(addr) if addrtype == 0: script = '76a9' # op_dup, op_hash_160 t@@ -600,70 +582,70 @@ class Transaction: raise return script - - def serialize(self, for_sig=None): + def input_script(self, txin, i, for_sig): # for_sig: # -1 : do not sign, estimate length # i>=0 : serialized tx for signing input i # None : add all known signatures + p2sh = txin.get('redeemScript') is not None + num_sig = txin['num_sig'] if p2sh else 1 + address = txin['address'] + + x_signatures = txin['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) + if for_sig == -1: + # we assume that signature will be 0x48 bytes long + pubkeys = txin['pubkeys'] + sig_list = [ "00" * 0x48 ] * num_sig + elif is_complete: + pubkeys = txin['pubkeys'] + sig_list = ((sig + '01') for sig in signatures) + else: + pubkeys = txin['x_pubkeys'] + 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: + 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' + script # put op_0 in front of script + redeem_script = self.multisig_script(pubkeys,2) + script += push_script(redeem_script) + + elif for_sig==i: + script = txin['redeemScript'] if p2sh else self.pay_script('address', address) + else: + script = '' + + return script + + + def serialize(self, for_sig=None): inputs = self.inputs outputs = self.outputs - s = int_to_hex(1,4) # version s += var_int( len(inputs) ) # number of inputs 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 - - p2sh = txin.get('redeemScript') is not None - num_sig = txin['num_sig'] - address = txin['address'] - - x_signatures = txin['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) - if for_sig == -1: - # we assume that signature will be 0x48 bytes long - pubkeys = txin['pubkeys'] - sig_list = [ "00" * 0x48 ] * num_sig - elif is_complete: - pubkeys = txin['pubkeys'] - sig_list = ((sig + '01') for sig in signatures) - else: - pubkeys = txin['x_pubkeys'] - 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: - 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' + script # put op_0 in front of script - redeem_script = self.multisig_script(pubkeys,2) - script += push_script(redeem_script) - - elif for_sig==i: - script = txin['redeemScript'] if p2sh else self.pay_script('address', address) - else: - script = '' - + s += int_to_hex(txin['prevout_n'], 4) # prev index + script = self.input_script(txin, i, for_sig) s += var_int( len(script)/2 ) # script length s += script s += "ffffffff" # sequence - s += var_int( len(outputs) ) # number of outputs for output in outputs: - type, addr, amount = output + output_type, addr, amount = output s += int_to_hex( amount, 8) # amount - script = self.pay_script(type, addr) + script = self.pay_script(output_type, addr) s += var_int( len(script)/2 ) # script length s += script # script s += int_to_hex(0,4) # lock time t@@ -696,9 +678,9 @@ class Transaction: for txin in self.inputs: if txin.get('is_coinbase'): continue - signatures = filter(None, txin['signatures']) + signatures = filter(None, txin.get('signatures',[])) s += len(signatures) - r += txin['num_sig'] + r += txin.get('num_sig',-1) return s, r def is_complete(self):