btc.tx.base.decodeScriptPubKey(): reimplement, parse nulldata correctly
This commit is contained in:
parent
acffdda309
commit
4b55f1158e
5 changed files with 54 additions and 23 deletions
|
|
@ -41,26 +41,40 @@ def decodeScriptPubKey(proto, s):
|
|||
# types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash
|
||||
ret = namedtuple('decoded_scriptPubKey', ['type', 'addr_fmt', 'addr', 'data'])
|
||||
|
||||
if len(s) == 50 and s[:6] == '76a914' and s[-4:] == '88ac':
|
||||
return ret('pubkeyhash', 'p2pkh', proto.pubhash2addr(bytes.fromhex(s[6:-4]), 'p2pkh'), None)
|
||||
|
||||
elif len(s) == 46 and s[:4] == 'a914' and s[-2:] == '87':
|
||||
return ret('scripthash', 'p2sh', proto.pubhash2addr(bytes.fromhex(s[4:-2]), 'p2sh'), None)
|
||||
|
||||
elif len(s) == 44 and s[:4] == proto.witness_vernum_hex + '14':
|
||||
return ret('witness_v0_keyhash', 'bech32', proto.pubhash2bech32addr(bytes.fromhex(s[4:])), None)
|
||||
|
||||
elif s[:2] == '6a': # OP_RETURN
|
||||
# range 1-80 == hex 2-160, plus 4 for opcode byte + push byte
|
||||
if 6 <= len(s) <= (proto.max_op_return_data_len * 2) + 6: # 2-160 -> 6-166
|
||||
return ret('nulldata', None, None, s[4:]) # return data in hex format
|
||||
else:
|
||||
raise ValueError('{}: OP_RETURN data bytes length not in range 1-{}'.format(
|
||||
len(s[4:]) // 2,
|
||||
match len(s):
|
||||
case 50 if s.startswith('76a914') and s.endswith('88ac'):
|
||||
return ret('pubkeyhash', 'p2pkh', proto.pubhash2addr(bytes.fromhex(s[6:-4]), 'p2pkh'), None)
|
||||
case 46 if s.startswith('a914') and s.endswith('87'):
|
||||
return ret('scripthash', 'p2sh', proto.pubhash2addr(bytes.fromhex(s[4:-2]), 'p2sh'), None)
|
||||
case 44 if s.startswith(proto.witness_vernum_hex + '14'):
|
||||
return ret(
|
||||
'witness_v0_keyhash',
|
||||
'bech32',
|
||||
proto.pubhash2bech32addr(bytes.fromhex(s[4:])),
|
||||
None)
|
||||
case 2 if s.startswith('6a'): # bare OP_RETURN
|
||||
return ret('nulldata', None, None, '')
|
||||
case x if s.startswith('6a'): # OP_RETURN with data
|
||||
# skip opcode byte + push byte(s): https://en.bitcoin.it/wiki/Script
|
||||
match int(s[2:4], 16):
|
||||
case y if 0 < y < 76:
|
||||
skip = 2
|
||||
case 76:
|
||||
skip = 3
|
||||
case 77:
|
||||
skip = 4
|
||||
case 78:
|
||||
skip = 6
|
||||
case y:
|
||||
raise ValueError(f'{y}: invalid first push byte in OP_RETURN data')
|
||||
if 1 <= (x >> 1) - skip <= proto.max_op_return_data_len:
|
||||
return ret('nulldata', None, None, s[skip * 2:]) # return data in hex format
|
||||
else:
|
||||
raise ValueError('{}: OP_RETURN data bytes length not in range 1-{}'.format(
|
||||
(x >> 1) - skip,
|
||||
proto.max_op_return_data_len))
|
||||
|
||||
else:
|
||||
raise NotImplementedError(f'Unrecognized scriptPubKey ({s})')
|
||||
case _:
|
||||
raise NotImplementedError(f'Unrecognized scriptPubKey ({s})')
|
||||
|
||||
def DeserializeTX(proto, txhex):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -415,7 +415,9 @@ class CmdTestMain(CmdTestBase, CmdTestShared):
|
|||
return self.addrgen(wf=None, dfl_wallet=True)
|
||||
|
||||
def txcreate_dfl_wallet(self, addrfile):
|
||||
return self.txcreate_common(sources=['15'])
|
||||
return self.txcreate_common(
|
||||
sources = ['15'],
|
||||
add_output_args = ['data:' + 'z' * self.proto.max_op_return_data_len])
|
||||
|
||||
def txsign_dfl_wallet(self, txfile, pf='', save=True, has_label=False):
|
||||
return self.txsign(None, txfile, save=save, has_label=has_label, dfl_wallet=True)
|
||||
|
|
@ -688,6 +690,7 @@ class CmdTestMain(CmdTestBase, CmdTestShared):
|
|||
do_label = False,
|
||||
ss_args = [],
|
||||
add_opts = [],
|
||||
add_output_args = [],
|
||||
view = 'n',
|
||||
addrs_per_wallet = addrs_per_wallet,
|
||||
non_mmgen_input_compressed = True,
|
||||
|
|
@ -725,6 +728,7 @@ class CmdTestMain(CmdTestBase, CmdTestShared):
|
|||
+ add_opts
|
||||
+ (make_input_opts() if cmdline_inputs else [])
|
||||
+ self._make_txcreate_outputs(tx_data)
|
||||
+ add_output_args
|
||||
+ [tx_data[num]['addrfile'] for num in tx_data]
|
||||
+ ss_args)
|
||||
|
||||
|
|
@ -765,7 +769,10 @@ class CmdTestMain(CmdTestBase, CmdTestShared):
|
|||
return t
|
||||
|
||||
def txcreate(self, addrfile):
|
||||
return self.txcreate_common(sources=['1'], add_opts=['--vsize-adj=1.01'])
|
||||
return self.txcreate_common(
|
||||
sources = ['1'],
|
||||
add_opts = ['--vsize-adj=1.01'],
|
||||
add_output_args = ['hexdata:' + 'ee' * self.proto.max_op_return_data_len])
|
||||
|
||||
def txbump(self, txfile, prepend_args=[], seed_args=[]):
|
||||
if not self.proto.cap('rbf'):
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ async def test_tx(tx_proto, tx_hex, desc, n):
|
|||
def has_nonstandard_outputs(outputs):
|
||||
for o in outputs:
|
||||
t = o['scriptPubKey']['type']
|
||||
if t in ('nonstandard', 'pubkey', 'nulldata'):
|
||||
if t in ('nonstandard', 'pubkey'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -151,6 +151,7 @@ class unit_tests:
|
|||
return await do_mmgen_ref(
|
||||
('btc', 'btc_tn'),
|
||||
(
|
||||
'test/ref/tx/B498CE[5.55788,38].rawtx',
|
||||
'test/ref/0B8D5A[15.31789,14,tl=1320969600].rawtx',
|
||||
'test/ref/0C7115[15.86255,14,tl=1320969600].testnet.rawtx',
|
||||
'test/ref/542169[5.68152,34].sigtx',
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ class unit_tests:
|
|||
'Bitcoin',
|
||||
(
|
||||
'tx/7A8157[6.65227,34].rawtx',
|
||||
'tx/B498CE[5.55788,38].rawtx',
|
||||
'tx/BB3FD2[7.57134314,123].sigtx',
|
||||
'tx/0A869F[1.23456,32].regtest.asubtx',
|
||||
),
|
||||
|
|
@ -112,12 +113,16 @@ class unit_tests:
|
|||
return True
|
||||
|
||||
def op_return_data(self, name, ut, desc='OpReturnData class'):
|
||||
max_len = cfg._proto.max_op_return_data_len
|
||||
from mmgen.proto.btc.tx.op_return_data import OpReturnData
|
||||
vecs = [
|
||||
'data:=:ETH.ETH:0x86d526d6624AbC0178cF7296cD538Ecc080A95F1:0/1/0',
|
||||
'hexdata:3d3a4554482e4554483a30783836643532366436363234416243303137'
|
||||
'38634637323936634435333845636330383041393546313a302f312f30',
|
||||
'hexdata:00010203040506',
|
||||
'hexdata:' + 'ee' * max_len,
|
||||
'data:' + 'z' * max_len,
|
||||
'data:a',
|
||||
'data:a\n',
|
||||
'data:a\tb',
|
||||
'data:' + gr_uc[:24],
|
||||
|
|
@ -135,17 +140,19 @@ class unit_tests:
|
|||
vmsg(repr(d))
|
||||
vmsg(d.hl())
|
||||
vmsg(d.hl(add_label=True))
|
||||
vmsg(f'length: {len(str(d))}')
|
||||
|
||||
bad_data = [
|
||||
'data:',
|
||||
'hexdata:',
|
||||
'data:' + ('x' * 81),
|
||||
'data:' + 'x' * (max_len + 1),
|
||||
'hexdata:' + ('deadbeef' * 20) + 'ee',
|
||||
'hex:0abc',
|
||||
'da:xyz',
|
||||
'hexdata:xyz',
|
||||
'hexdata:abcde',
|
||||
b'data:abc',
|
||||
'hexdata:' + 'dd' * (max_len + 1),
|
||||
]
|
||||
|
||||
def bad(n):
|
||||
|
|
@ -164,6 +171,7 @@ class unit_tests:
|
|||
('bad7', 'AssertionError', 'not in hex', bad(6)),
|
||||
('bad8', 'AssertionError', 'even', bad(7)),
|
||||
('bad9', 'AssertionError', 'a string', bad(8)),
|
||||
('bad10', 'AssertionError', 'not in range', bad(9)),
|
||||
), pfx='')
|
||||
|
||||
return True
|
||||
|
|
|
|||
1
test/ref/tx/B498CE[5.55788,38].rawtx
Normal file
1
test/ref/tx/B498CE[5.55788,38].rawtx
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"MMGenTransaction":{"coin_id":"BTC","chain":"mainnet","txid":"B498CE","send_amt":"5.55788","timestamp":"20250929_134955","blockcount":0,"serialized":"0200000001f887cf2ebaf99b7c2bccc8e6b1b2c57ff1481ce52a0c9f4baa1b517ee07322050600000000fdffffff040000000000000000536a4c50beadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafe0006f9000000000017a914727a067c447400c9b1487057a50cf0652299bd9b87e0a027200000000016001402d345bc687a1d02b651d11c4a64c3c91f51ba2262c64ded00000000160014cd11bb484e10f1da9e453c302d79b6cf219a81a000000000","inputs":[{"vout":6,"txid":"052273e07e511baa4b9f0c2ae51c48f17fc5b2b1e6c8cc2b7c9bf9ba2ecf87f8","scriptPubKey":"0014761f0cd436e84ca10c38d63a0ff318a49aa72f58","amt":"45.3709525","comment":"Ian\u2019s inheritance","addr":"bc1qwc0se4pkapx2zrpc6caqlucc5jd2wt6cj9fwnv","confs":354535,"mmid":"225E3732:B:5","sequence":4294967293}],"outputs":[{"amt":"0","data":"hexdata:beadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafebeadcafe"},{"addr":"3C8K8t4kfED7eXQKVJikhUA4UHgoo24C15","amt":"0.1632"},{"addr":"bc1qqtf5t0rg0gws9dj36ywy5exrey04rw3zfmxjcy","amt":"5.39468","mmid":"225E3732:B:12"},{"addr":"bc1qe5gmkjzwzrca48j98scz67dkeuse4qdqdzh5a4","amt":"39.8129725","is_chg":true,"mmid":"225E3732:B:99"}]},"chksum":"650a8f"}
|
||||
Loading…
Add table
Add a link
Reference in a new issue