1
0

support native segwit transactions

This commit is contained in:
ThomasV
2017-09-01 14:15:54 +02:00
parent 68873d92f9
commit d9f2edf6b0
6 changed files with 120 additions and 61 deletions

View File

@@ -116,6 +116,19 @@ class BCDataStream(object):
def write_int64(self, val): return self._write_num('<q', val)
def write_uint64(self, val): return self._write_num('<Q', val)
def read_push_size(self):
size = self.input[self.read_cursor]
self.read_cursor += 1
if size < 0x4c:
return size
if size == 0x4c:
nsize = self.read_bytes(1)[0]
elif size == 0x4d:
nsize = self._read_num('<H')
elif size == 0x4e:
nsize = self._read_num('<I')
return nsize
def read_compact_size(self):
size = self.input[self.read_cursor]
self.read_cursor += 1
@@ -309,9 +322,6 @@ def parse_scriptSig(d, _bytes):
d['address'] = bitcoin.hash160_to_p2sh(bitcoin.hash_160(item))
d['type'] = 'p2wpkh-p2sh'
d['redeemScript'] = redeemScript
d['x_pubkeys'] = ["(witness)"]
d['pubkeys'] = ["(witness)"]
d['signatures'] = ['(witness)']
d['num_sig'] = 1
else:
# payto_pubkey
@@ -350,7 +360,19 @@ def parse_scriptSig(d, _bytes):
print_error("cannot find address in input script", bh2u(_bytes))
return
x_sig = [bh2u(x[1]) for x in decoded[1:-1]]
dec2 = [ x for x in script_GetOp(decoded[-1][1]) ]
m, n, x_pubkeys, pubkeys, redeemScript = parse_redeemScript(decoded[-1][1])
# 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(bfh(redeemScript)))
def parse_redeemScript(s):
dec2 = [ x for x in script_GetOp(s) ]
m = dec2[0][0] - opcodes.OP_1 + 1
n = dec2[-2][0] - opcodes.OP_1 + 1
op_m = opcodes.OP_1 + m - 1
@@ -362,15 +384,7 @@ def parse_scriptSig(d, _bytes):
x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]]
pubkeys = [safe_parse_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(bfh(redeemScript)))
return m, n, x_pubkeys, pubkeys, redeemScript
def get_address_from_output_script(_bytes):
decoded = [x for x in script_GetOp(_bytes)]
@@ -395,7 +409,7 @@ def get_address_from_output_script(_bytes):
# segwit address
match = [ opcodes.OP_0, opcodes.OP_PUSHDATA4 ]
if match_decoded(decoded, match):
return TYPE_ADDRESS, hash160_to_segwit_addr(decoded[1][1])
return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1])
return TYPE_SCRIPT, bh2u(_bytes)
@@ -406,7 +420,6 @@ def parse_input(vds):
prevout_n = vds.read_uint32()
scriptSig = vds.read_bytes(vds.read_compact_size())
sequence = vds.read_uint32()
d['scriptSig'] = bh2u(scriptSig)
d['prevout_hash'] = prevout_hash
d['prevout_n'] = prevout_n
d['sequence'] = sequence
@@ -420,12 +433,30 @@ def parse_input(vds):
d['type'] = 'unknown'
d['num_sig'] = 0
if scriptSig:
parse_scriptSig(d, scriptSig)
if len(scriptSig) == 8:
d['value'] = struct.unpack_from('<Q', scriptSig, 0)[0]
d['scriptSig'] = ''
else:
d['scriptSig'] = bh2u(scriptSig)
parse_scriptSig(d, scriptSig)
return d
def parse_witness(vds):
def parse_witness(vds, txin):
n = vds.read_compact_size()
return list(vds.read_bytes(vds.read_compact_size()) for i in range(n))
w = list(bh2u(vds.read_bytes(vds.read_push_size())) for i in range(n))
if n > 2:
txin['num_sig'] = n - 2
txin['signatures'] = parse_sig(w[1:-1])
m, n, x_pubkeys, pubkeys, witnessScript = parse_redeemScript(bfh(w[-1]))
txin['x_pubkeys'] = x_pubkeys
txin['pubkeys'] = pubkeys
txin['witnessScript'] = witnessScript
else:
txin['num_sig'] = 1
txin['pubkeys'] = [ w[-1] ]
txin['signatures'] = parse_sig([w[:-1]])
def parse_output(vds, i):
d = {}
@@ -451,19 +482,24 @@ def deserialize(raw):
n_vin = vds.read_compact_size()
d['inputs'] = [parse_input(vds) for i in range(n_vin)]
n_vout = vds.read_compact_size()
d['outputs'] = [parse_output(vds,i) for i in range(n_vout)]
d['outputs'] = [parse_output(vds, i) for i in range(n_vout)]
if is_segwit:
d['witness'] = [parse_witness(vds) for i in range(n_vin)]
for i in range(n_vin):
txin = d['inputs'][i]
parse_witness(vds, txin)
if not txin.get('scriptSig'):
if txin['num_sig'] == 1:
txin['type'] = 'p2wpkh'
txin['address'] = bitcoin.public_key_to_p2wpkh(bfh(txin['pubkeys'][0]))
else:
txin['type'] = 'p2wsh'
txin['address'] = bitcoin.script_to_p2wsh(txin['witnessScript'])
d['lockTime'] = vds.read_uint32()
return d
# pay & redeem scripts
def segwit_script(pubkey):
pubkey = safe_parse_pubkey(pubkey)
pkh = bh2u(hash_160(bfh(pubkey)))
@@ -536,12 +572,7 @@ class Transaction:
for i, txin in enumerate(self.inputs()):
pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
sigs1 = txin.get('signatures')
if d.get('witness') is None:
sigs2 = d['inputs'][i].get('signatures')
else:
# signatures are in the witnesses. But the last item is
# the pubkey or the multisig script, so skip that.
sigs2 = d['witness'][i][:-1]
sigs2 = d['inputs'][i].get('signatures')
for sig in sigs2:
if sig in sigs1:
continue
@@ -622,12 +653,18 @@ class Transaction:
@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)
if txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
n = 2
return var_int(n) + push_script(sig_list[0]) + push_script(pubkeys[0])
elif txin['type'] in ['p2wsh', 'p2wsh-p2sh']:
n = len(sig_list) + 2
# fixme: witness script must be decided by wallet
witness_script = multisig_script(pubkeys, txin['num_sig'])
return var_int(n) + '00' + ''.join(push_script(x) for x in sig_list) + push_script(witness_script)
@classmethod
def is_segwit_input(self, txin):
return txin['type'] in ['p2wpkh-p2sh']
return txin['type'] in ['p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh']
@classmethod
def input_script(self, txin, estimate_size=False):
@@ -645,7 +682,10 @@ class Transaction:
script += push_script(redeem_script)
elif _type == 'p2pkh':
script += push_script(pubkeys[0])
elif _type == 'p2wpkh-p2sh':
elif _type in ['p2wpkh', 'p2wsh']:
# if it is not complete we store the value
return '' if self.is_txin_complete(txin) or estimate_size else int_to_hex(txin['value'], 8)
elif _type in ['p2wpkh-p2sh', 'p2wsh-p2sh']:
redeem_script = txin.get('redeemScript') or segwit_script(pubkeys[0])
return push_script(redeem_script)
elif _type == 'address':
@@ -654,15 +694,22 @@ class Transaction:
return txin['scriptSig']
return script
@classmethod
def is_txin_complete(self, txin):
num_sig = txin.get('num_sig', 1)
x_signatures = txin['signatures']
signatures = list(filter(None, x_signatures))
return len(signatures) == num_sig
@classmethod
def get_preimage_script(self, txin):
# only for non-segwit
if txin['type'] == 'p2pkh':
return bitcoin.address_to_script(txin['address'])
elif txin['type'] == 'p2sh':
elif txin['type'] in ['p2sh', 'p2wsh']:
pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin)
return multisig_script(pubkeys, txin['num_sig'])
elif txin['type'] == 'p2wpkh-p2sh':
elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']:
pubkey = txin['pubkeys'][0]
pkh = bh2u(bitcoin.hash_160(bfh(pubkey)))
return '76a9' + push_script(pkh) + '88ac'