Support bech32 and bech32m (taproot) addresses in the query command

Coin.BECH32_HRP (plm, tplm, rplm) and a BIP173/BIP350 segwit decoder let
address_to_hashX read segwit addresses. The witness transaction fixtures are
no longer skipped.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-23 15:32:17 +02:00
1 parent 2ef6a7abcb
commit b7ce0efad1
6 files changed
+170 -10

No files matched your search

+76 -1
View File
@@ -4,6 +4,8 @@ import pytest
from electrumx.lib import tx as lib_tx
from electrumx.lib.coins import Coin, Palladium, PalladiumTestnet, PalladiumRegtest
from electrumx.lib.hash import Base58Error, Bech32, Bech32Error
from electrumx.lib.script import ScriptPubKey
# (class, net, rpc port, P2PKH verbyte, P2SH verbyte, genesis hash)
PARAMS = [
@@ -35,7 +37,80 @@ def test_lookup_coin_class(coin, net):
def test_daemon_requirements_disabled():
# Palladium Core (Bitcoin Core 0.19 based) has no getindexinfo/txospenderindex
# Palladium Core has no getindexinfo/txospenderindex
for coin, *_ in PARAMS:
assert coin.MIN_REQUIRED_DAEMON_VERSION is None
assert not coin.REQUIRED_DAEMON_INDEXES
def bech32_encode_v0(hrp, program):
"""Minimal bech32 encoder of a witness v0 address, only to build test addresses"""
charset = Bech32.CHARSET
acc = bits = 0
data = [0]
for byte in program:
acc = (acc << 8) | byte
bits += 8
while bits >= 5:
bits -= 5
data.append((acc >> bits) & 31)
if bits:
data.append((acc << (5 - bits)) & 31)
values = [ord(c) >> 5 for c in hrp] + [0] + [ord(c) & 31 for c in hrp] + data
polymod = Bech32._polymod(values + [0] * 6) ^ Bech32.BECH32_CONST
checksum = [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
return hrp + "1" + "".join(charset[d] for d in data + checksum)
# bech32 / bech32m: the valid and invalid segwit addresses of BIP 173 and BIP 350
BIP_VALID = [
("bc", "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", 0, "751e76e8199196d454941c45d1b3a323f1433bd6"),
("tb", "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", 0,
"1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"),
("bc", "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", 1,
"751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"),
("bc", "BC1SW50QGDZ25J", 16, "751e"),
]
BIP_INVALID = [
("bc", "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut"), # wrong hrp
("bc", "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd"), # bech32 for version 1
("bc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh"), # bech32m for version 0
("bc", "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL"), # bad checksum
("bc", "Bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"), # mixed case
("bc", "bc1gmk9yu"), # empty program
("bc", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"), # bad checksum
]
@pytest.mark.parametrize("hrp,address,witver,program", BIP_VALID)
def test_bech32_decode_segwit(hrp, address, witver, program):
assert Bech32.decode_segwit(hrp, address) == (witver, bytes.fromhex(program))
@pytest.mark.parametrize("hrp,address", BIP_INVALID)
def test_bech32_decode_segwit_invalid(hrp, address):
with pytest.raises(Bech32Error):
Bech32.decode_segwit(hrp, address)
def test_segwit_script():
assert ScriptPubKey.segwit_script(0, bytes(20)) == b"\x00\x14" + bytes(20)
assert ScriptPubKey.segwit_script(1, bytes(32)) == b"\x51\x20" + bytes(32)
assert ScriptPubKey.segwit_script(16, b"\x75\x1e") == b"\x60\x02\x75\x1e"
@pytest.mark.parametrize("coin,hrp", [(Palladium, "plm"), (PalladiumTestnet, "tplm"), (PalladiumRegtest, "rplm")])
def test_bech32_address_to_script(coin, hrp):
assert coin.BECH32_HRP == hrp
# a v0 witness keyhash address built from a known hash160 (the checksum is computed with a constant)
h160 = bytes.fromhex("751e76e8199196d454941c45d1b3a323f1433bd6")
address = bech32_encode_v0(hrp, h160)
assert coin.pay_to_address_script(address) == b"\x00\x14" + h160
assert coin.pay_to_address_script(address.upper()) == b"\x00\x14" + h160
assert coin.address_to_hashX(address) == coin.hashX_from_script(b"\x00\x14" + h160)
def test_bech32_address_of_another_network_is_rejected():
address = bech32_encode_v0("tplm", bytes(20))
with pytest.raises((Base58Error, Bech32Error)): # not a mainnet bech32 address, so it is read as base58
Palladium.pay_to_address_script(address)