diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index 7a993765..4e872dfd 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -31,15 +31,22 @@ def addr2scriptPubKey(proto, addr): 'bech32': proto.witness_vernum_hex + '14' + decode_addr(proto, addr) }[addr.addr_fmt] -def scriptPubKey2addr(proto, s): +def decodeScriptPubKey(proto, s): + # src/wallet/rpc/addresses.cpp: + # 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 proto.pubhash2addr(bytes.fromhex(s[6:-4]), 'p2pkh'), 'p2pkh' + 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 proto.pubhash2addr(bytes.fromhex(s[4:-2]), 'p2sh'), 'p2sh' + 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 proto.pubhash2bech32addr(bytes.fromhex(s[4:])), 'bech32' + return ret('witness_v0_keyhash', 'bech32', proto.pubhash2bech32addr(bytes.fromhex(s[4:])), None) + else: - raise NotImplementedError(f'Unknown scriptPubKey ({s})') + raise NotImplementedError(f'Unrecognized scriptPubKey ({s})') def DeserializeTX(proto, txhex): """ @@ -119,7 +126,7 @@ def DeserializeTX(proto, txhex): } for i in range(d['num_txouts'])]) for o in d['txouts']: - o['address'] = scriptPubKey2addr(proto, o['scriptPubKey'])[0] + o.update(decodeScriptPubKey(proto, o['scriptPubKey'])._asdict()) if has_witness: # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki @@ -317,7 +324,7 @@ class Base(TxBase.Base): check_equal( 'outputs', - sorted((o['address'], o['amt']) for o in dtx.txouts), + sorted((o['addr'], o['amt']) for o in dtx.txouts), sorted((o.addr, o.amt) for o in self.outputs)) if str(self.txid) != make_chksum_6(bytes.fromhex(dtx.unsigned_hex)).upper(): diff --git a/mmgen/proto/btc/tx/completed.py b/mmgen/proto/btc/tx/completed.py index ea55cc55..a154c12d 100755 --- a/mmgen/proto/btc/tx/completed.py +++ b/mmgen/proto/btc/tx/completed.py @@ -15,7 +15,7 @@ proto.btc.tx.completed: Bitcoin completed transaction class from ....tx import completed as TxBase from ....obj import HexStr from ....util import msg, die -from .base import Base, scriptPubKey2addr +from .base import Base, decodeScriptPubKey class Completed(Base, TxBase.Completed): fn_fee_unit = 'satoshi' @@ -45,17 +45,17 @@ class Completed(Base, TxBase.Completed): def check_pubkey_scripts(self): for n, i in enumerate(self.inputs, 1): - addr, fmt = scriptPubKey2addr(self.proto, i.scriptPubKey) - if i.addr != addr: - if fmt != i.addr.addr_fmt: + ds = decodeScriptPubKey(self.proto, i.scriptPubKey) + if ds.addr != i.addr: + if ds.addr_fmt != i.addr.addr_fmt: m = 'Address format of scriptPubKey ({}) does not match that of address ({}) in input #{}' - msg(m.format(fmt, i.addr.addr_fmt, n)) + msg(m.format(ds.addr_fmt, i.addr.addr_fmt, n)) m = 'ERROR: Address and scriptPubKey of transaction input #{} do not match!' die(3, (m+'\n {:23}{}'*3).format( n, 'address:', i.addr, 'scriptPubKey:', i.scriptPubKey, - 'scriptPubKey->address:', addr)) + 'scriptPubKey->address:', ds.addr)) # def is_replaceable_from_rpc(self): # dec_tx = await self.rpc.call('decoderawtransaction', self.serialized) diff --git a/mmgen/tool/coin.py b/mmgen/tool/coin.py index 4d30228a..bc722fbb 100755 --- a/mmgen/tool/coin.py +++ b/mmgen/tool/coin.py @@ -184,8 +184,8 @@ class tool_cmd(tool_cmd_base): def scriptpubkey2addr(self, hexstr: 'sstr'): "convert scriptPubKey to coin address" - from ..proto.btc.tx.base import scriptPubKey2addr - return scriptPubKey2addr(self.proto, hexstr)[0] + from ..proto.btc.tx.base import decodeScriptPubKey + return decodeScriptPubKey(self.proto, hexstr).addr def eth_checksummed_addr(self, addr: 'sstr'): "create a checksummed Ethereum address" diff --git a/test/daemontest_d/ut_tx.py b/test/daemontest_d/ut_tx.py index ef7353a8..8751d746 100755 --- a/test/daemontest_d/ut_tx.py +++ b/test/daemontest_d/ut_tx.py @@ -75,7 +75,7 @@ async def test_tx(tx_proto, tx_hex, desc, n): for i in range(len(a)): if 'addresses' in a[i]['scriptPubKey']: A = a[i]['scriptPubKey']['addresses'][0] - B = b[i]['address'] + B = b[i]['addr'] fs = 'address of output {} does not match\nA: {}\nB: {}' assert A == B, fs.format(i, A, B)