proto.btc.tx.base: scriptPubKey2addr() -> decodeScriptPubKey()

This commit is contained in:
The MMGen Project 2025-02-06 10:12:49 +00:00
commit e3dd55e909
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
4 changed files with 23 additions and 16 deletions

View file

@ -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():

View file

@ -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)

View file

@ -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"

View file

@ -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)