From e0d04af154ff42825c92c308fc34fc2026748529 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Wed, 29 Apr 2026 10:07:37 +0200 Subject: [PATCH] constants: add BitcoinPurple (BTCP) and BitcoinPurpleTestnet network classes Add per-chain PoW fields to AbstractNet (DIFFICULTY_ADJUSTMENT_INTERVAL, POW_TARGET_TIMESPAN, MAX_TARGET, MAX_ADJUSTMENT_FACTOR, POW_GENESIS_BITS) with Bitcoin defaults so existing chains are unaffected. Add BitcoinPurple and BitcoinPurpleTestnet as independent AbstractNet subclasses with BTCP-specific parameters: - 120-block retarget interval, 7200-second target timespan - powLimit 0x1e0fffff, genesis nBits 0x1e0ffff0 (POW_GENESIS_BITS) - P2PKH prefix 56 (0x38), P2SH 55 (0x37), WIF 0xb7, bech32 HRP "btcp"/"tbtcp" - SLIP-0132 HD key headers identical to Bitcoin mainnet/testnet respectively - ElectrumX default ports 50001/50002 (mainnet) and 60001/60002 (testnet) Add chain data directories: - electrum/chains/bitcoinpurple/{servers,checkpoints,fallback_lnnodes}.json - electrum/chains/bitcoinpurple_testnet/{servers,checkpoints,fallback_lnnodes}.json servers.json is populated with 5 known BTCP ElectrumX nodes (TCP 51001, SSL 51002) --- .../chains/bitcoinpurple/checkpoints.json | 1 + .../bitcoinpurple/fallback_lnnodes.json | 1 + electrum/chains/bitcoinpurple/servers.json | 32 +++++++ .../bitcoinpurple_testnet/checkpoints.json | 1 + .../fallback_lnnodes.json | 1 + .../chains/bitcoinpurple_testnet/servers.json | 1 + electrum/constants.py | 85 +++++++++++++++++++ 7 files changed, 122 insertions(+) create mode 100644 electrum/chains/bitcoinpurple/checkpoints.json create mode 100644 electrum/chains/bitcoinpurple/fallback_lnnodes.json create mode 100644 electrum/chains/bitcoinpurple/servers.json create mode 100644 electrum/chains/bitcoinpurple_testnet/checkpoints.json create mode 100644 electrum/chains/bitcoinpurple_testnet/fallback_lnnodes.json create mode 100644 electrum/chains/bitcoinpurple_testnet/servers.json diff --git a/electrum/chains/bitcoinpurple/checkpoints.json b/electrum/chains/bitcoinpurple/checkpoints.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/electrum/chains/bitcoinpurple/checkpoints.json @@ -0,0 +1 @@ +[] diff --git a/electrum/chains/bitcoinpurple/fallback_lnnodes.json b/electrum/chains/bitcoinpurple/fallback_lnnodes.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/electrum/chains/bitcoinpurple/fallback_lnnodes.json @@ -0,0 +1 @@ +{} diff --git a/electrum/chains/bitcoinpurple/servers.json b/electrum/chains/bitcoinpurple/servers.json new file mode 100644 index 000000000..275fb0eb7 --- /dev/null +++ b/electrum/chains/bitcoinpurple/servers.json @@ -0,0 +1,32 @@ +{ + "173.212.224.67": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.4.2" + }, + "144.91.120.225": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.4.2" + }, + "66.94.115.80": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.4.2" + }, + "89.117.149.130": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.4.2" + }, + "84.247.169.248": { + "pruning": "-", + "s": "51002", + "t": "51001", + "version": "1.4.2" + } +} diff --git a/electrum/chains/bitcoinpurple_testnet/checkpoints.json b/electrum/chains/bitcoinpurple_testnet/checkpoints.json new file mode 100644 index 000000000..fe51488c7 --- /dev/null +++ b/electrum/chains/bitcoinpurple_testnet/checkpoints.json @@ -0,0 +1 @@ +[] diff --git a/electrum/chains/bitcoinpurple_testnet/fallback_lnnodes.json b/electrum/chains/bitcoinpurple_testnet/fallback_lnnodes.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/electrum/chains/bitcoinpurple_testnet/fallback_lnnodes.json @@ -0,0 +1 @@ +{} diff --git a/electrum/chains/bitcoinpurple_testnet/servers.json b/electrum/chains/bitcoinpurple_testnet/servers.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/electrum/chains/bitcoinpurple_testnet/servers.json @@ -0,0 +1 @@ +{} diff --git a/electrum/constants.py b/electrum/constants.py index b4b88d11b..99c56b588 100644 --- a/electrum/constants.py +++ b/electrum/constants.py @@ -81,6 +81,16 @@ class AbstractNet: XPUB_HEADERS: Mapping[str, int] XPUB_HEADERS_INV: Mapping[int, str] + # PoW difficulty parameters (Bitcoin defaults; override per chain as needed) + DIFFICULTY_ADJUSTMENT_INTERVAL: int = 2016 # blocks per retarget window + POW_TARGET_TIMESPAN: int = 14 * 24 * 60 * 60 # target seconds per window + MAX_TARGET: int = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff # compact 0x1d00ffff + MAX_ADJUSTMENT_FACTOR: int = 4 + # When genesis nBits != target_to_bits(MAX_TARGET), set this to the genesis + # nBits value so get_target(-1) returns the correct initial difficulty. + # None means "derive from MAX_TARGET" (correct for Bitcoin where they match). + POW_GENESIS_BITS: Optional[int] = None + @classmethod def max_checkpoint(cls) -> int: return max(0, len(cls.CHECKPOINTS) * 2016 - 1) @@ -259,6 +269,81 @@ class BitcoinMutinynet(BitcoinTestnet): LN_DNS_SEEDS = [] +class BitcoinPurple(AbstractNet): + + NET_NAME = "bitcoinpurple" + TESTNET = False + WIF_PREFIX = 0xb7 + ADDRTYPE_P2PKH = 56 + ADDRTYPE_P2SH = 55 + SEGWIT_HRP = "btcp" + BOLT11_HRP = SEGWIT_HRP + GENESIS = "000003823fbf82ea4906cbe214617ce7a70a5da29c19ecb1d65618bcf04ec015" + DEFAULT_PORTS = {'t': '50001', 's': '50002'} + BLOCK_HEIGHT_FIRST_LIGHTNING_CHANNELS = 0 + + XPRV_HEADERS = { + 'standard': 0x0488ade4, # xprv + 'p2wpkh-p2sh': 0x049d7878, # yprv + 'p2wsh-p2sh': 0x0295b005, # Yprv + 'p2wpkh': 0x04b2430c, # zprv + 'p2wsh': 0x02aa7a99, # Zprv + } + XPRV_HEADERS_INV = inv_dict(XPRV_HEADERS) + XPUB_HEADERS = { + 'standard': 0x0488b21e, # xpub + 'p2wpkh-p2sh': 0x049d7cb2, # ypub + 'p2wsh-p2sh': 0x0295b43f, # Ypub + 'p2wpkh': 0x04b24746, # zpub + 'p2wsh': 0x02aa7ed3, # Zpub + } + XPUB_HEADERS_INV = inv_dict(XPUB_HEADERS) + + # Provisional BIP44 coin type (not SLIP-0044 registered; matches BTCP P2P port) + BIP44_COIN_TYPE = 13496 + LN_REALM_BYTE = 0 + LN_DNS_SEEDS = [] + + # BTCP retargets every 120 blocks with a 7200-second target timespan + DIFFICULTY_ADJUSTMENT_INTERVAL = 120 + POW_TARGET_TIMESPAN = 7200 # 120 * 60 seconds + # powLimit: absolute upper bound for any target (compact 0x1e0fffff) + MAX_TARGET = 0x00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + MAX_ADJUSTMENT_FACTOR = 4 + # Genesis nBits is 0x1e0ffff0, which is stricter than powLimit (0x1e0fffff). + # Period-0 blocks must carry this exact bits value, so we record it here. + POW_GENESIS_BITS = 0x1e0ffff0 + + +class BitcoinPurpleTestnet(BitcoinPurple): + + NET_NAME = "bitcoinpurple_testnet" + TESTNET = True + SEGWIT_HRP = "tbtcp" + BOLT11_HRP = SEGWIT_HRP + GENESIS = "000002fdc3921c1ad368816fcc587f499698d42b42ab5a5d94ee67882ef9d998" + DEFAULT_PORTS = {'t': '60001', 's': '60002'} + BIP44_COIN_TYPE = 1 + LN_REALM_BYTE = 1 + + XPRV_HEADERS = { + 'standard': 0x04358394, # tprv + 'p2wpkh-p2sh': 0x044a4e28, # uprv + 'p2wsh-p2sh': 0x024285b5, # Uprv + 'p2wpkh': 0x045f18bc, # vprv + 'p2wsh': 0x02575048, # Vprv + } + XPRV_HEADERS_INV = inv_dict(XPRV_HEADERS) + XPUB_HEADERS = { + 'standard': 0x043587cf, # tpub + 'p2wpkh-p2sh': 0x044a5262, # upub + 'p2wsh-p2sh': 0x024289ef, # Upub + 'p2wpkh': 0x045f1cf6, # vpub + 'p2wsh': 0x02575483, # Vpub + } + XPUB_HEADERS_INV = inv_dict(XPUB_HEADERS) + + NETS_LIST = tuple(all_subclasses(AbstractNet)) # type: Sequence[Type[AbstractNet]] NETS_LIST = tuple(sorted(NETS_LIST, key=lambda x: x.NET_NAME))