Files
plm-electrumx/tests/lib/test_palladium.py
T
davideandClaude Sonnet 5.5 b32548fd01 Fix mempool.get_info, testmempoolaccept and outpoint.subscribe with the Palladium daemon
- mempool.get_info: the daemon has no incrementalrelayfee in getmempoolinfo,
  read incrementalfee from getnetworkinfo
- testmempoolaccept: the daemon does not report wtxid
- outpoint.subscribe: refuse the subscription (Coin.DAEMON_HAS_TXO_SPENDER_INDEX)
  instead of closing the session at the next block, when the daemon errors

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 16:17:43 +02:00

123 lines
5.4 KiB
Python

# Tests of the Palladium coin classes (parameters of the Palladium Core chain params)
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 = [
(Palladium, "mainnet", 2332, "37", "05",
"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
(PalladiumTestnet, "testnet", 12332, "7f", "73",
"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943"),
(PalladiumRegtest, "regtest", 28443, "7f", "73",
"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206"),
]
@pytest.mark.parametrize("coin,net,rpc_port,p2pkh,p2sh,genesis", PARAMS)
def test_chain_params(coin, net, rpc_port, p2pkh, p2sh, genesis):
assert coin.NAME == "Palladium"
assert coin.NET == net
assert coin.RPC_PORT == rpc_port
assert coin.P2PKH_VERBYTE == bytes.fromhex(p2pkh)
assert coin.P2SH_VERBYTES == (bytes.fromhex(p2sh),)
assert coin.GENESIS_HASH == genesis
assert coin.BASIC_HEADER_SIZE == 80
assert coin.DESERIALIZER is lib_tx.DeserializerSegWit
@pytest.mark.parametrize("coin,shortname", [(Palladium, "PLM"), (PalladiumTestnet, "TPLM"), (PalladiumRegtest, "RPLM")])
def test_shortname(coin, shortname):
assert coin.SHORTNAME == shortname
@pytest.mark.parametrize("coin,net", [(c, n) for c, n, *_ in PARAMS])
def test_lookup_coin_class(coin, net):
assert Coin.lookup_coin_class("Palladium", net) is coin
assert Coin.lookup_coin_class("palladium", net.upper()) is coin
def test_daemon_requirements_disabled():
# Palladium Core has no getindexinfo/txospenderindex
for coin, *_ in PARAMS:
assert coin.MIN_REQUIRED_DAEMON_VERSION is None
assert not coin.REQUIRED_DAEMON_INDEXES
assert coin.DAEMON_HAS_TXO_SPENDER_INDEX is False
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)