#!/usr/bin/env python # # Electrum - lightweight Bitcoin client # Copyright (C) 2011 Thomas Voegtlin # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation files # (the "Software"), to deal in the Software without restriction, # including without limitation the rights to use, copy, modify, merge, # publish, distribute, sublicense, and/or sell copies of the Software, # and to permit persons to whom the Software is furnished to do so, # subject to the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # Note: The deserialization code originally comes from ABE. import bitcoin from bitcoin import * from bitcoin import hash160_to_p2sh, hash160_to_p2pkh from util import print_error, profiler import time import sys import struct # # Workalike python implementation of Bitcoin's CDataStream class. # import struct import StringIO import random from keystore import xpubkey_to_address, xpubkey_to_pubkey NO_SIGNATURE = 'ff' class SerializationError(Exception): """ Thrown when there's a problem deserializing or serializing """ class BCDataStream(object): def __init__(self): self.input = None self.read_cursor = 0 def clear(self): self.input = None self.read_cursor = 0 def write(self, bytes): # Initialize with string of bytes if self.input is None: self.input = bytes else: self.input += bytes def read_string(self): # Strings are encoded depending on length: # 0 to 252 : 1-byte-length followed by bytes (if any) # 253 to 65,535 : byte'253' 2-byte-length followed by bytes # 65,536 to 4,294,967,295 : byte '254' 4-byte-length followed by bytes # ... and the Bitcoin client is coded to understand: # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string # ... but I don't think it actually handles any strings that big. if self.input is None: raise SerializationError("call write(bytes) before trying to deserialize") try: length = self.read_compact_size() except IndexError: raise SerializationError("attempt to read past end of buffer") return self.read_bytes(length) def write_string(self, string): # Length-encoded as with read-string self.write_compact_size(len(string)) self.write(string) def read_bytes(self, length): try: result = self.input[self.read_cursor:self.read_cursor+length] self.read_cursor += length return result except IndexError: raise SerializationError("attempt to read past end of buffer") return '' def read_boolean(self): return self.read_bytes(1)[0] != chr(0) def read_int16(self): return self._read_num('= opcodes.OP_SINGLEBYTE_END: opcode <<= 8 opcode |= ord(bytes[i]) i += 1 if opcode <= opcodes.OP_PUSHDATA4: nSize = opcode if opcode == opcodes.OP_PUSHDATA1: nSize = ord(bytes[i]) i += 1 elif opcode == opcodes.OP_PUSHDATA2: (nSize,) = struct.unpack_from(' 0: result += " " if opcode <= opcodes.OP_PUSHDATA4: result += "%d:"%(opcode,) result += short_hex(vch) else: result += script_GetOpName(opcode) return result def match_decoded(decoded, to_match): if len(decoded) != len(to_match): return False; for i in range(len(decoded)): if to_match[i] == opcodes.OP_PUSHDATA4 and decoded[i][0] <= opcodes.OP_PUSHDATA4 and decoded[i][0]>0: continue # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent. if to_match[i] != decoded[i][0]: return False return True def parse_sig(x_sig): s = [] for sig in x_sig: if sig[-2:] == '01': s.append(sig[:-2]) else: assert sig == NO_SIGNATURE s.append(None) return s def parse_scriptSig(d, bytes): try: decoded = [ x for x in script_GetOp(bytes) ] except Exception: # coinbase transactions raise an exception print_error("cannot find address in input script", bytes.encode('hex')) return match = [ opcodes.OP_PUSHDATA4 ] if match_decoded(decoded, match): item = decoded[0][1] if item[0] == chr(0): redeemScript = item.encode('hex') d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(redeemScript.decode('hex'))) d['type'] = 'p2wpkh-p2sh' d['redeemScript'] = redeemScript d['x_pubkeys'] = ["(witness)"] d['pubkeys'] = ["(witness)"] d['signatures'] = ['(witness)'] d['num_sig'] = 1 else: # payto_pubkey d['type'] = 'p2pk' d['address'] = "(pubkey)" d['signatures'] = [item.encode('hex')] d['num_sig'] = 1 d['x_pubkeys'] = ["(pubkey)"] d['pubkeys'] = ["(pubkey)"] return # non-generated TxIn transactions push a signature # (seventy-something bytes) and then their public key # (65 bytes) onto the stack: match = [ opcodes.OP_PUSHDATA4, opcodes.OP_PUSHDATA4 ] if match_decoded(decoded, match): sig = decoded[0][1].encode('hex') x_pubkey = decoded[1][1].encode('hex') try: signatures = parse_sig([sig]) pubkey, address = xpubkey_to_address(x_pubkey) except: import traceback traceback.print_exc(file=sys.stdout) print_error("cannot find address in input script", bytes.encode('hex')) return d['type'] = 'p2pkh' d['signatures'] = signatures d['x_pubkeys'] = [x_pubkey] d['num_sig'] = 1 d['pubkeys'] = [pubkey] d['address'] = address return # p2sh transaction, m of n 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 = [x[1].encode('hex') for x in decoded[1:-1]] dec2 = [ x for x in script_GetOp(decoded[-1][1]) ] m = dec2[0][0] - opcodes.OP_1 + 1 n = dec2[-2][0] - opcodes.OP_1 + 1 op_m = opcodes.OP_1 + m - 1 op_n = opcodes.OP_1 + n - 1 match_multisig = [ op_m ] + [opcodes.OP_PUSHDATA4]*n + [ op_n, opcodes.OP_CHECKMULTISIG ] if not match_decoded(dec2, match_multisig): print_error("cannot find address in input script", bytes.encode('hex')) return x_pubkeys = map(lambda x: x[1].encode('hex'), dec2[1:-2]) pubkeys = [xpubkey_to_pubkey(x) for x in x_pubkeys] redeemScript = multisig_script(pubkeys, m) # write result in d d['type'] = 'p2sh' d['num_sig'] = m d['signatures'] = parse_sig(x_sig) d['x_pubkeys'] = x_pubkeys d['pubkeys'] = pubkeys d['redeemScript'] = redeemScript d['address'] = hash160_to_p2sh(hash_160(redeemScript.decode('hex'))) def get_address_from_output_script(bytes): decoded = [ x for x in script_GetOp(bytes) ] # The Genesis Block, self-payments, and pay-by-IP-address payments look like: # 65 BYTES:... CHECKSIG match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ] if match_decoded(decoded, match): return TYPE_PUBKEY, decoded[0][1].encode('hex') # Pay-by-Bitcoin-address TxOuts look like: # DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ] if match_decoded(decoded, match): return TYPE_ADDRESS, hash160_to_p2pkh(decoded[2][1]) # p2sh match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ] if match_decoded(decoded, match): return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1]) return TYPE_SCRIPT, bytes def parse_input(vds): d = {} prevout_hash = hash_encode(vds.read_bytes(32)) prevout_n = vds.read_uint32() scriptSig = vds.read_bytes(vds.read_compact_size()) d['scriptSig'] = scriptSig.encode('hex') sequence = vds.read_uint32() if prevout_hash == '00'*32: d['is_coinbase'] = True else: d['is_coinbase'] = False d['prevout_hash'] = prevout_hash d['prevout_n'] = prevout_n d['sequence'] = sequence d['pubkeys'] = [] d['signatures'] = {} d['address'] = None if scriptSig: parse_scriptSig(d, scriptSig) return d def parse_witness(vds): n = vds.read_compact_size() for i in range(n): x = vds.read_bytes(vds.read_compact_size()) def parse_output(vds, i): d = {} d['value'] = vds.read_int64() scriptPubKey = vds.read_bytes(vds.read_compact_size()) d['type'], d['address'] = get_address_from_output_script(scriptPubKey) d['scriptPubKey'] = scriptPubKey.encode('hex') d['prevout_n'] = i return d def deserialize(raw): vds = BCDataStream() vds.write(raw.decode('hex')) d = {} start = vds.read_cursor d['version'] = vds.read_int32() n_vin = vds.read_compact_size() is_segwit = (n_vin == 0) if is_segwit: marker = vds.read_bytes(1) assert marker == chr(1) n_vin = vds.read_compact_size() d['inputs'] = list(parse_input(vds) for i in xrange(n_vin)) n_vout = vds.read_compact_size() d['outputs'] = list(parse_output(vds,i) for i in xrange(n_vout)) if is_segwit: d['witness'] = list(parse_witness(vds) for i in xrange(n_vin)) d['lockTime'] = vds.read_uint32() return d # pay & redeem scripts def push_script(x): return op_push(len(x)/2) + x def get_scriptPubKey(addr): addrtype, hash_160 = bc_address_to_hash_160(addr) if addrtype == bitcoin.ADDRTYPE_P2PKH: script = '76a9' # op_dup, op_hash_160 script += push_script(hash_160.encode('hex')) script += '88ac' # op_equalverify, op_checksig elif addrtype == bitcoin.ADDRTYPE_P2SH: script = 'a9' # op_hash_160 script += push_script(hash_160.encode('hex')) script += '87' # op_equal else: raise BaseException('unknown address type') return script def segwit_script(pubkey): pkh = hash_160(pubkey.decode('hex')).encode('hex') return '00' + push_script(pkh) def multisig_script(public_keys, m): n = len(public_keys) assert n <= 15 assert m <= n op_m = format(opcodes.OP_1 + m - 1, 'x') op_n = format(opcodes.OP_1 + n - 1, 'x') keylist = [op_push(len(k)/2) + k for k in public_keys] return op_m + ''.join(keylist) + op_n + 'ae' class Transaction: def __str__(self): if self.raw is None: self.raw = self.serialize() return self.raw def __init__(self, raw): if raw is None: self.raw = None elif type(raw) in [str, unicode]: self.raw = raw.strip() if raw else None elif type(raw) is dict: self.raw = raw['hex'] else: raise BaseException("cannot initialize transaction", raw) self._inputs = None self._outputs = None self.locktime = 0 def update(self, raw): self.raw = raw self._inputs = None self.deserialize() def inputs(self): if self._inputs is None: self.deserialize() return self._inputs def outputs(self): if self._outputs is None: self.deserialize() return self._outputs @classmethod def get_sorted_pubkeys(self, txin): # sort pubkeys and x_pubkeys, using the order of pubkeys x_pubkeys = txin['x_pubkeys'] pubkeys = txin.get('pubkeys') if pubkeys is None: pubkeys = [xpubkey_to_pubkey(x) for x in x_pubkeys] pubkeys, x_pubkeys = zip(*sorted(zip(pubkeys, x_pubkeys))) txin['pubkeys'] = pubkeys = list(pubkeys) txin['x_pubkeys'] = x_pubkeys = list(x_pubkeys) return pubkeys, x_pubkeys def update_signatures(self, raw): """Add new signatures to a transaction""" d = deserialize(raw) for i, txin in enumerate(self.inputs()): pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) sigs1 = txin.get('signatures') sigs2 = d['inputs'][i].get('signatures') for sig in sigs2: if sig in sigs1: continue pre_hash = Hash(self.serialize_preimage(i).decode('hex')) # der to string order = ecdsa.ecdsa.generator_secp256k1.order() r, s = ecdsa.util.sigdecode_der(sig.decode('hex'), order) sig_string = ecdsa.util.sigencode_string(r, s, order) compressed = True for recid in range(4): public_key = MyVerifyingKey.from_signature(sig_string, recid, pre_hash, curve = SECP256k1) pubkey = point_to_ser(public_key.pubkey.point, compressed).encode('hex') if pubkey in pubkeys: public_key.verify_digest(sig_string, pre_hash, sigdecode = ecdsa.util.sigdecode_string) j = pubkeys.index(pubkey) print_error("adding sig", i, j, pubkey, sig) self._inputs[i]['signatures'][j] = sig self._inputs[i]['x_pubkeys'][j] = pubkey break # redo raw self.raw = self.serialize() def deserialize(self): if self.raw is None: return #self.raw = self.serialize() if self._inputs is not None: return d = deserialize(self.raw) self._inputs = d['inputs'] self._outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']] self.locktime = d['lockTime'] return d @classmethod def from_io(klass, inputs, outputs, locktime=0): self = klass(None) self._inputs = inputs self._outputs = outputs self.locktime = locktime return self @classmethod def pay_script(self, output_type, addr): if output_type == TYPE_SCRIPT: return addr.encode('hex') elif output_type == TYPE_ADDRESS: return get_scriptPubKey(addr) else: raise TypeError('Unknown output type') return script @classmethod def get_siglist(self, txin, estimate_size=False): # if we have enough signatures, we use the actual pubkeys # otherwise, use extended pubkeys (with bip32 derivation) num_sig = txin.get('num_sig', 1) if estimate_size: # we assume that signature will be 0x48 bytes long pk_list = [ "00" * 0x21 ] * num_sig sig_list = [ "00" * 0x48 ] * num_sig else: pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) x_signatures = txin['signatures'] signatures = filter(None, x_signatures) is_complete = len(signatures) == num_sig if is_complete: pk_list = pubkeys sig_list = [(sig + '01') for sig in signatures] else: pk_list = x_pubkeys sig_list = [(sig + '01') if sig else NO_SIGNATURE for sig in x_signatures] return pk_list, sig_list @classmethod def serialize_witness(self, txin): pubkeys, sig_list = self.get_siglist(txin) n = len(pubkeys) + len(sig_list) return var_int(n) + ''.join(push_script(x) for x in sig_list) + ''.join(push_script(x) for x in pubkeys) @classmethod def is_segwit_input(self, txin): return txin['type'] in ['p2wpkh-p2sh'] @classmethod def input_script(self, txin, estimate_size=False): _type = txin['type'] pubkeys, sig_list = self.get_siglist(txin, estimate_size) script = ''.join(push_script(x) for x in sig_list) if _type == 'p2pk': pass elif _type == 'p2sh': # put op_0 before script script = '00' + script redeem_script = multisig_script(pubkeys, txin['num_sig']) script += push_script(redeem_script) elif _type == 'p2pkh': script += push_script(pubkeys[0]) elif _type == 'p2wpkh-p2sh': redeem_script = segwit_script(pubkeys[0]) return push_script(redeem_script) else: raise TypeError('Unknown txin type', _type) return script @classmethod def get_preimage_script(self, txin): # only for non-segwit if txin['type'] == 'p2pkh': return get_scriptPubKey(txin['address']) elif txin['type'] == 'p2sh': pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) return multisig_script(pubkeys, txin['num_sig']) else: raise TypeError('Unknown txin type', _type) @classmethod def serialize_outpoint(self, txin): return txin['prevout_hash'].decode('hex')[::-1].encode('hex') + int_to_hex(txin['prevout_n'], 4) @classmethod def serialize_input(self, txin, script): # Prev hash and index s = self.serialize_outpoint(txin) # Script length, script, sequence s += var_int(len(script)/2) s += script s += int_to_hex(txin.get('sequence', 0xffffffff), 4) return s def set_sequence(self, n): for txin in self.inputs(): txin['sequence'] = n def BIP_LI01_sort(self): # See https://github.com/kristovatlas/rfc/blob/master/bips/bip-li01.mediawiki self._inputs.sort(key = lambda i: (i['prevout_hash'], i['prevout_n'])) self._outputs.sort(key = lambda o: (o[2], self.pay_script(o[0], o[1]))) def serialize_output(self, output): output_type, addr, amount = output s = int_to_hex(amount, 8) script = self.pay_script(output_type, addr) s += var_int(len(script)/2) s += script return s def serialize_preimage(self, i): nVersion = int_to_hex(1, 4) nHashType = int_to_hex(1, 4) nLocktime = int_to_hex(self.locktime, 4) inputs = self.inputs() outputs = self.outputs() txin = inputs[i] if self.is_segwit_input(txin): hashPrevouts = Hash(''.join(self.serialize_outpoint(txin) for txin in inputs).decode('hex')).encode('hex') hashSequence = Hash(''.join(int_to_hex(txin.get('sequence', 0xffffffff), 4) for txin in inputs).decode('hex')).encode('hex') hashOutputs = Hash(''.join(self.serialize_output(o) for o in outputs).decode('hex')).encode('hex') outpoint = self.serialize_outpoint(txin) pubkey = txin['pubkeys'][0] pkh = bitcoin.hash_160(pubkey.decode('hex')).encode('hex') redeemScript = '00' + push_script(pkh) scriptCode = push_script('76a9' + push_script(pkh) + '88ac') script_hash = bitcoin.hash_160(redeemScript.decode('hex')).encode('hex') scriptPubKey = 'a9' + push_script(script_hash) + '87' amount = int_to_hex(txin['value'], 8) nSequence = int_to_hex(txin.get('sequence', 0xffffffff), 4) preimage = nVersion + hashPrevouts + hashSequence + outpoint + scriptCode + amount + nSequence + hashOutputs + nLocktime + nHashType else: txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, self.get_preimage_script(txin) if i==k else '') for k, txin in enumerate(inputs)) txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs) preimage = nVersion + txins + txouts + nLocktime + nHashType return preimage def is_segwit(self): return any(self.is_segwit_input(x) for x in self.inputs()) def serialize(self, estimate_size=False, witness=True): nVersion = int_to_hex(1, 4) nLocktime = int_to_hex(self.locktime, 4) inputs = self.inputs() outputs = self.outputs() txins = var_int(len(inputs)) + ''.join(self.serialize_input(txin, self.input_script(txin, estimate_size)) for txin in inputs) txouts = var_int(len(outputs)) + ''.join(self.serialize_output(o) for o in outputs) if witness and self.is_segwit(): marker = '00' flag = '01' witness = ''.join(self.serialize_witness(x) for x in inputs) return nVersion + marker + flag + txins + txouts + witness + nLocktime else: return nVersion + txins + txouts + nLocktime def hash(self): print "warning: deprecated tx.hash()" return self.txid() def txid(self): all_segwit = all(self.is_segwit_input(x) for x in self.inputs()) if not all_segwit and not self.is_complete(): return None ser = self.serialize(witness=False) return Hash(ser.decode('hex'))[::-1].encode('hex') def wtxid(self): ser = self.serialize(witness=True) return Hash(ser.decode('hex'))[::-1].encode('hex') def add_inputs(self, inputs): self._inputs.extend(inputs) self.raw = None def add_outputs(self, outputs): self._outputs.extend(outputs) self.raw = None def input_value(self): return sum(x['value'] for x in self.inputs()) def output_value(self): return sum(val for tp, addr, val in self.outputs()) def get_fee(self): return self.input_value() - self.output_value() def is_final(self): return not any([x.get('sequence', 0xffffffff) < 0xffffffff - 1 for x in self.inputs()]) @profiler def estimated_size(self): '''Return an estimated tx size in bytes.''' return len(self.serialize(True)) / 2 if not self.is_complete() or self.raw is None else len(self.raw) / 2 # ASCII hex string @classmethod def estimated_input_size(self, txin): '''Return an estimated of serialized input size in bytes.''' script = self.input_script(txin, True) return len(self.serialize_input(txin, script)) / 2 def signature_count(self): r = 0 s = 0 for txin in self.inputs(): if txin.get('is_coinbase'): continue signatures = filter(None, txin.get('signatures',[])) s += len(signatures) r += txin.get('num_sig',-1) return s, r def is_complete(self): s, r = self.signature_count() return r == s def inputs_without_script(self): out = set() for i, txin in enumerate(self.inputs()): if txin.get('scriptSig') == '': out.add(i) return out def sign(self, keypairs): for i, txin in enumerate(self.inputs()): num = txin['num_sig'] pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) for j, x_pubkey in enumerate(x_pubkeys): signatures = filter(None, txin['signatures']) if len(signatures) == num: # txin is complete break fd_key = 'fd00' + bitcoin.hash_160(pubkeys[j].decode('hex')).encode('hex') if x_pubkey in keypairs.keys() or fd_key in keypairs.keys(): print_error("adding signature for", x_pubkey) sec = keypairs.get(x_pubkey) or keypairs.get(fd_key) pubkey = public_key_from_private_key(sec) assert pubkey == pubkeys[j] # add signature pre_hash = Hash(self.serialize_preimage(i).decode('hex')) pkey = regenerate_key(sec) secexp = pkey.secret private_key = bitcoin.MySigningKey.from_secret_exponent(secexp, curve = SECP256k1) public_key = private_key.get_verifying_key() sig = private_key.sign_digest_deterministic(pre_hash, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_der) assert public_key.verify_digest(sig, pre_hash, sigdecode = ecdsa.util.sigdecode_der) txin['signatures'][j] = sig.encode('hex') txin['x_pubkeys'][j] = pubkey self._inputs[i] = txin print_error("is_complete", self.is_complete()) self.raw = self.serialize() def get_outputs(self): """convert pubkeys to addresses""" o = [] for type, x, v in self.outputs(): if type == TYPE_ADDRESS: addr = x elif type == TYPE_PUBKEY: addr = bitcoin.public_key_to_p2pkh(x.decode('hex')) else: addr = 'SCRIPT ' + x.encode('hex') o.append((addr,v)) # consider using yield (addr, v) return o def get_output_addresses(self): return [addr for addr, val in self.get_outputs()] def has_address(self, addr): return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs())) def as_dict(self): if self.raw is None: self.raw = self.serialize() self.deserialize() out = { 'hex': self.raw, 'complete': self.is_complete(), 'final': self.is_final(), } return out def requires_fee(self, wallet): # see https://en.bitcoin.it/wiki/Transaction_fees # # size must be smaller than 1 kbyte for free tx size = len(self.serialize(-1))/2 if size >= 10000: return True # all outputs must be 0.01 BTC or larger for free tx for addr, value in self.get_outputs(): if value < 1000000: return True # priority must be large enough for free tx threshold = 57600000 weight = 0 for txin in self.inputs(): height, conf, timestamp = wallet.get_tx_height(txin["prevout_hash"]) weight += txin["value"] * conf priority = weight / size print_error(priority, threshold) return priority < threshold def tx_from_str(txt): "json or raw hexadecimal" import json txt = txt.strip() try: txt.decode('hex') is_hex = True except: is_hex = False if is_hex: return txt tx_dict = json.loads(str(txt)) assert "hex" in tx_dict.keys() return tx_dict["hex"]