From 26d759364f3685c482d37795075a162024998ce9 Mon Sep 17 00:00:00 2001 From: zebra-lucky Date: Sun, 30 Nov 2025 16:52:05 +0200 Subject: [PATCH] add testnet4 support --- scripts/joinmarket-qt.py | 2 ++ scripts/qtsupport.py | 2 +- src/jmbitcoin/secp256k1_deterministic.py | 6 ++++-- src/jmbitcoin/secp256k1_main.py | 5 +++-- src/jmclient/blockchaininterface.py | 2 +- src/jmclient/configure.py | 11 +++++++---- src/jmclient/cryptoengine.py | 10 ++++++---- src/jmclient/wallet.py | 7 ++++--- src/jmclient/yieldgenerator.py | 7 ++++--- src/jmdaemon/irc.py | 2 ++ 10 files changed, 34 insertions(+), 20 deletions(-) diff --git a/scripts/joinmarket-qt.py b/scripts/joinmarket-qt.py index 2d9d30d..bca75b9 100755 --- a/scripts/joinmarket-qt.py +++ b/scripts/joinmarket-qt.py @@ -2646,6 +2646,8 @@ async def main(): mainWindow.resize(600, 500) if get_network() == 'testnet': suffix = ' - Testnet' + elif get_network() == 'testnet4': + suffix = ' - Testnet4' elif get_network() == 'signet': suffix = ' - Signet' else: diff --git a/scripts/qtsupport.py b/scripts/qtsupport.py index c97fb44..196a473 100644 --- a/scripts/qtsupport.py +++ b/scripts/qtsupport.py @@ -63,7 +63,7 @@ config_types = {'rpc_port': int, 'absurd_fee_per_kb': 'amount'} config_tips = { 'blockchain_source': 'options: bitcoin-rpc, regtest (for testing)', - 'network': 'one of "signet", "testnet" or "mainnet"', + 'network': 'one of "signet", "testnet", "testnet4" or "mainnet"', 'checktx': 'whether to check fees before completing transaction', 'rpc_host': 'the host for bitcoind; only used if blockchain_source is bitcoin-rpc', diff --git a/src/jmbitcoin/secp256k1_deterministic.py b/src/jmbitcoin/secp256k1_deterministic.py index 34545d4..fd99a3a 100644 --- a/src/jmbitcoin/secp256k1_deterministic.py +++ b/src/jmbitcoin/secp256k1_deterministic.py @@ -12,8 +12,10 @@ TESTNET_PRIVATE = b'\x04\x35\x83\x94' TESTNET_PUBLIC = b'\x04\x35\x87\xCF' SIGNET_PRIVATE = b'\x04\x35\x83\x94' SIGNET_PUBLIC = b'\x04\x35\x87\xCF' -PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE, SIGNET_PRIVATE] -PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC, SIGNET_PUBLIC] +TESTNET4_PRIVATE = b'\x04\x35\x83\x94' +TESTNET4_PUBLIC = b'\x04\x35\x87\xCF' +PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE, SIGNET_PRIVATE, TESTNET4_PRIVATE] +PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC, SIGNET_PUBLIC, TESTNET4_PUBLIC] privtopub = privkey_to_pubkey diff --git a/src/jmbitcoin/secp256k1_main.py b/src/jmbitcoin/secp256k1_main.py index 44204f9..7ba15b4 100644 --- a/src/jmbitcoin/secp256k1_main.py +++ b/src/jmbitcoin/secp256k1_main.py @@ -22,8 +22,9 @@ secp_obj.lib.secp256k1_ec_pubkey_tweak_mul.argtypes = [ctypes.c_void_p, ctypes.c N = 115792089237316195423570985008687907852837564279074904382605163141518161494337 BTC_P2PK_VBYTE = {"mainnet": b'\x00', "testnet": b'\x6f', "signet": b'\x6f', - "regtest": 100} -BTC_P2SH_VBYTE = {"mainnet": b'\x05', "testnet": b'\xc4', "signet": b'\xc4'} + "regtest": 100, "testnet4": b'\x6f'} +BTC_P2SH_VBYTE = {"mainnet": b'\x05', "testnet": b'\xc4', "signet": b'\xc4', + "testnet4": b'\xc4'} """PoDLE related primitives """ diff --git a/src/jmclient/blockchaininterface.py b/src/jmclient/blockchaininterface.py index 5b90c18..76f80e9 100644 --- a/src/jmclient/blockchaininterface.py +++ b/src/jmclient/blockchaininterface.py @@ -341,7 +341,7 @@ class BitcoinCoreInterface(BlockchainInterface): actualNet = blockchainInfo['chain'] netmap = {'main': 'mainnet', 'test': 'testnet', 'regtest': 'regtest', - 'signet': 'signet'} + 'signet': 'signet', 'testnet4': 'testnet4'} if netmap[actualNet] != network and \ (not (actualNet == "regtest" and network == "testnet")): #special case of regtest and testnet having the same addr format diff --git a/src/jmclient/configure.py b/src/jmclient/configure.py index 0888902..4d15029 100644 --- a/src/jmclient/configure.py +++ b/src/jmclient/configure.py @@ -121,12 +121,13 @@ use_ssl = false # to Bitcoin Core; note that use of this option for any other purpose is currently unsupported. blockchain_source = bitcoin-rpc -# options: signet, testnet, mainnet +# options: signet, testnet, testnet4, mainnet # Note: for regtest, use network = testnet network = mainnet rpc_host = localhost -# default ports are 8332 for mainnet, 18443 for regtest, 18332 for testnet, 38332 for signet +# default ports are 8332 for mainnet, 18443 for regtest, 18332 for testnet, +# 38332 for signet, 48332 for testnet4 rpc_port = # Use either rpc_user / rpc_password pair or rpc_cookie_file. @@ -759,7 +760,7 @@ def load_program_config(config_path: str = "", bs: Optional[str] = None, "location cmtdata/commitments.json") if get_network() != "mainnet": # no need to be flexible for tests; note this is used - # for regtest, signet and testnet3 + # for regtest, signet, testnet3 and testnet4 global_singleton.commit_file_location = "cmtdata/" + get_network() + \ "_commitments.json" set_commitment_file(os.path.join(config_path, @@ -860,7 +861,7 @@ def get_blockchain_interface_instance(_config: ConfigParser, *, BitcoinCoreNoHistoryInterface source = _config.get("BLOCKCHAIN", "blockchain_source") network = get_network() - testnet = (network == 'testnet' or network == 'signet') + testnet = (network in ['testnet', 'testnet4', 'signet']) if source in ('bitcoin-rpc', 'regtest', 'bitcoin-rpc-no-history'): rpc_host = _config.get("BLOCKCHAIN", "rpc_host") @@ -872,6 +873,8 @@ def get_blockchain_interface_instance(_config: ConfigParser, *, rpc_port = 18443 elif network == 'testnet': rpc_port = 18332 + elif network == 'testnet4': + rpc_port = 48332 elif network == 'signet': rpc_port = 38332 else: diff --git a/src/jmclient/cryptoengine.py b/src/jmclient/cryptoengine.py index 1f7f172..4974b1c 100644 --- a/src/jmclient/cryptoengine.py +++ b/src/jmclient/cryptoengine.py @@ -21,11 +21,13 @@ TYPE_P2PKH, TYPE_P2SH_P2WPKH, TYPE_P2WPKH, TYPE_P2SH_M_N, TYPE_TIMELOCK_P2WSH, \ TYPE_WATCHONLY_TIMELOCK_P2WSH, TYPE_WATCHONLY_P2WPKH, TYPE_P2WSH, \ TYPE_P2TR, TYPE_P2TR_FROST, TYPE_TAPROOT_WALLET_FIDELITY_BONDS, \ TYPE_TAPROOT_WATCHONLY_FIDELITY_BONDS, TYPE_WATCHONLY_P2TR, = range(15) -NET_MAINNET, NET_TESTNET, NET_SIGNET = range(3) +NET_MAINNET, NET_TESTNET, NET_SIGNET, NET_TESTNET4 = range(4) NET_MAP = {'mainnet': NET_MAINNET, 'testnet': NET_TESTNET, - 'signet': NET_SIGNET} -WIF_PREFIX_MAP = {'mainnet': b'\x80', 'testnet': b'\xef', 'signet': b'\xef'} -BIP44_COIN_MAP = {'mainnet': 2**31, 'testnet': 2**31 + 1, 'signet': 2**31 + 1} + 'signet': NET_SIGNET, 'testnet4': NET_TESTNET4} +WIF_PREFIX_MAP = {'mainnet': b'\x80', 'testnet': b'\xef', 'signet': b'\xef', + 'testnet4': b'\xef'} +BIP44_COIN_MAP = {'mainnet': 2**31, 'testnet': 2**31 + 1, 'signet': 2**31 + 1, + 'testnet4': 2**31 + 1} BIP32_PUB_PREFIX = "xpub" BIP49_PUB_PREFIX = "ypub" diff --git a/src/jmclient/wallet.py b/src/jmclient/wallet.py index 1ade3cd..c7e05c0 100644 --- a/src/jmclient/wallet.py +++ b/src/jmclient/wallet.py @@ -771,7 +771,7 @@ class BaseWallet(object): assert self._cache is not None assert self.max_mixdepth is not None assert self.max_mixdepth >= 0 - assert self.network in ('mainnet', 'testnet', 'signet') + assert self.network in ('mainnet', 'testnet', 'signet', 'testnet4') if mixdepth is not None: assert mixdepth >= 0 @@ -834,12 +834,13 @@ class BaseWallet(object): args: storage: a Storage object - network: str, network we are on, 'mainnet', 'testnet' or 'signet' + network: str, network we are on, 'mainnet', 'testnet', 'signet' + or 'testnet4' max_mixdepth: int, number of the highest mixdepth timestamp: bytes or None, defaults to the current time write: execute storage.save() """ - assert network in ('mainnet', 'testnet', 'signet') + assert network in ('mainnet', 'testnet', 'signet', 'testnet4') assert max_mixdepth >= 0 if storage.data != {}: diff --git a/src/jmclient/yieldgenerator.py b/src/jmclient/yieldgenerator.py index 742aaa9..8f7d772 100644 --- a/src/jmclient/yieldgenerator.py +++ b/src/jmclient/yieldgenerator.py @@ -498,8 +498,8 @@ async def ygmain(ygclass, nickserv_password='', gaplimit=6): if jm_single().config.get("BLOCKCHAIN", "network") == "mainnet": jlog.error("You have enabled SNICKER on mainnet, this is not " "yet supported for yieldgenerators; either use " - "signet/regtest/testnet, or run SNICKER manually " - "with snicker/receive-snicker.py.") + "signet/regtest/testnet/testnet4, or run SNICKER " + "manually with snicker/receive-snicker.py.") twisted_sys_exit(EXIT_ARGERROR) snicker_r = SNICKERReceiver(wallet_service) servers = jm_single().config.get("SNICKER", "servers").split(",") @@ -508,7 +508,8 @@ async def ygmain(ygclass, nickserv_password='', gaplimit=6): snicker_factory = None nodaemon = jm_single().config.getint("DAEMON", "no_daemon") daemon = True if nodaemon == 1 else False - if jm_single().config.get("BLOCKCHAIN", "network") in ["regtest", "testnet", "signet"]: + if jm_single().config.get("BLOCKCHAIN", "network") in [ + "regtest", "testnet", "signet", "testnet4"]: startLogging(sys.stdout) start_reactor(jm_single().config.get("DAEMON", "daemon_host"), diff --git a/src/jmdaemon/irc.py b/src/jmdaemon/irc.py index f00c34c..5a0a3a4 100644 --- a/src/jmdaemon/irc.py +++ b/src/jmdaemon/irc.py @@ -47,6 +47,8 @@ def get_config_irc_channel(chan_name, btcnet): channel = "#" + chan_name if btcnet == "testnet": channel += "-test" + elif btcnet == "testnet4": + channel += "-test4" elif btcnet == "signet": channel += "-sig" return channel