From 4f11e830af951df123afd4ba2088f2061622cfc9 Mon Sep 17 00:00:00 2001 From: nahuhh <50635951+nahuhh@users.noreply.github.com> Date: Thu, 20 Mar 2025 01:22:58 +0000 Subject: [PATCH 1/8] nmc: chainparams --- basicswap/chainparams.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/basicswap/chainparams.py b/basicswap/chainparams.py index 7ed0935..82d7ad4 100644 --- a/basicswap/chainparams.py +++ b/basicswap/chainparams.py @@ -251,6 +251,7 @@ chainparams = { "rpcport": 8336, "pubkey_address": 52, "script_address": 13, + "key_prefix": 180, "hrp": "nc", "bip44": 7, "min_amount": 100000, @@ -260,6 +261,7 @@ chainparams = { "rpcport": 18336, "pubkey_address": 111, "script_address": 196, + "key_prefix": 239, "hrp": "tn", "bip44": 1, "min_amount": 100000, @@ -270,6 +272,7 @@ chainparams = { "rpcport": 18443, "pubkey_address": 111, "script_address": 196, + "key_prefix": 239, "hrp": "ncrt", "bip44": 1, "min_amount": 100000, From 3ffe55e5a273bc6ad4dcec19f055a1afe039f17d Mon Sep 17 00:00:00 2001 From: nahuhh <50635951+nahuhh@users.noreply.github.com> Date: Thu, 20 Mar 2025 01:23:22 +0000 Subject: [PATCH 2/8] nmc: prepare --- basicswap/bin/prepare.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index b5f3a11..5b6ab47 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -82,6 +82,9 @@ FIRO_VERSION_TAG = os.getenv("FIRO_VERSION_TAG", "") NAV_VERSION = os.getenv("NAV_VERSION", "7.0.3") NAV_VERSION_TAG = os.getenv("NAV_VERSION_TAG", "") +NMC_VERSION = os.getenv("NAV_VERSION", "28.0") +NMC_VERSION_TAG = os.getenv("NAV_VERSION_TAG", "") + DCR_VERSION = os.getenv("DCR_VERSION", "1.8.1") DCR_VERSION_TAG = os.getenv("DCR_VERSION_TAG", "") @@ -103,7 +106,7 @@ known_coins = { "bitcoin": (BITCOIN_VERSION, BITCOIN_VERSION_TAG, ("laanwj",)), "litecoin": (LITECOIN_VERSION, LITECOIN_VERSION_TAG, ("davidburkett38",)), "decred": (DCR_VERSION, DCR_VERSION_TAG, ("decred_release",)), - "namecoin": ("0.18.0", "", ("JeremyRand",)), + "namecoin": (NMC_VERSION, NMC_VERSION_TAG, ("RoseTuring",)), "monero": (MONERO_VERSION, MONERO_VERSION_TAG, ("binaryfate",)), "wownero": (WOWNERO_VERSION, WOWNERO_VERSION_TAG, ("wowario",)), "pivx": (PIVX_VERSION, PIVX_VERSION_TAG, ("fuzzbawls",)), @@ -115,8 +118,7 @@ known_coins = { } disabled_coins = [ - "navcoin", - "namecoin", # Needs update + "navcoin" ] expected_key_ids = { @@ -124,6 +126,7 @@ expected_key_ids = { "thrasher": ("FE3348877809386C",), "laanwj": ("1E4AED62986CD25D",), "JeremyRand": ("2DBE339E29F6294C",), + "RoseTuring": ("9FE3BFDDA6C53495",), "binaryfate": ("F0AF4D462A0BDF92",), "wowario": ("793504B449C69220",), "davidburkett38": ("3620E9D387E55666",), @@ -228,6 +231,9 @@ DCR_RPC_PWD = os.getenv("DCR_RPC_PWD", random.randbytes(random.randint(14, 18)). NMC_RPC_HOST = os.getenv("NMC_RPC_HOST", "127.0.0.1") NMC_RPC_PORT = int(os.getenv("NMC_RPC_PORT", 19698)) +NMC_ONION_PORT = int(os.getenv("NMC_ONION_PORT", 9698)) +NMC_RPC_USER = os.getenv("NMC_RPC_USER", "") +NMC_RPC_PWD = os.getenv("NMC_RPC_PWD", "") PIVX_RPC_HOST = os.getenv("PIVX_RPC_HOST", "127.0.0.1") PIVX_RPC_PORT = int(os.getenv("PIVX_RPC_PORT", 51473)) @@ -931,16 +937,10 @@ def prepareCore(coin, version_data, settings, data_dir, extra_opts={}): % (version, assert_filename) ) elif coin == "namecoin": - release_url = "https://beta.namecoin.org/files/namecoin-core/namecoin-core-{}/{}".format( - version, release_filename - ) - assert_filename = "{}-{}-{}-build.assert".format( - coin, os_name, version.rsplit(".", 1)[0] - ) - assert_url = ( - "https://raw.githubusercontent.com/namecoin/gitian.sigs/master/%s-%s/%s/%s" - % (version, os_dir_name, signing_key_name, assert_filename) - ) + release_url = f"https://beta.namecoin.org/files/namecoin-core/namecoin-core-{version}/{release_filename}" + signing_key = "Rose%20Turing" + assert_filename = "noncodesigned.SHA256SUMS" + assert_url = f"https://raw.githubusercontent.com/namecoin/guix.sigs/main/{version}/{signing_key}/{assert_filename}" elif coin == "pivx": release_filename = "{}-{}-{}.{}".format(coin, version, BIN_ARCH, FILE_EXT) release_url = ( @@ -1804,6 +1804,7 @@ def initialise_wallets( Coins.DOGE, Coins.DCR, Coins.DASH, + Coins.NMC, ) # Always start Particl, it must be running to initialise a wallet in addcoin mode # Particl must be loaded first as subsequent coins are initialised from the Particl mnemonic @@ -2453,14 +2454,15 @@ def main(): "manage_daemon": shouldManageDaemon("NMC"), "rpchost": NMC_RPC_HOST, "rpcport": NMC_RPC_PORT + port_offset, + "onionport": NMC_ONION_PORT + port_offset, "datadir": os.getenv("NMC_DATA_DIR", os.path.join(data_dir, "namecoin")), "bindir": os.path.join(bin_dir, "namecoin"), - "use_segwit": False, - "use_csv": False, + "use_segwit": True, + "use_csv": True, "blocks_confirmed": 1, "conf_target": 2, "core_version_no": getKnownVersion("namecoin"), - "core_version_group": 18, + "core_version_group": 28, "chain_lookups": "local", }, "monero": { From 8967f677c39fa406e64896ca1f062fefa3fec60d Mon Sep 17 00:00:00 2001 From: tecnovert Date: Wed, 26 Mar 2025 12:25:04 +0200 Subject: [PATCH 3/8] nmc: Create bdb wallet. --- basicswap/basicswap.py | 2 +- basicswap/bin/prepare.py | 3 +- basicswap/config.py | 9 +-- basicswap/pgp/keys/namecoin_JeremyRand.pgp | 14 ---- basicswap/pgp/keys/namecoin_RoseTuring.pgp | 52 +++++++++++++ tests/basicswap/extended/test_nmc.py | 85 ++++++++++++++-------- 6 files changed, 110 insertions(+), 55 deletions(-) delete mode 100644 basicswap/pgp/keys/namecoin_JeremyRand.pgp create mode 100644 basicswap/pgp/keys/namecoin_RoseTuring.pgp diff --git a/basicswap/basicswap.py b/basicswap/basicswap.py index 39b3b35..8a5e1d4 100644 --- a/basicswap/basicswap.py +++ b/basicswap/basicswap.py @@ -391,7 +391,7 @@ class BasicSwap(BaseApp): Coins.PART_BLIND, Coins.BCH, ) - self.coins_without_segwit = (Coins.PIVX, Coins.DASH, Coins.NMC) + self.coins_without_segwit = (Coins.PIVX, Coins.DASH) # TODO: Adjust ranges self.min_delay_event = self.get_int_setting("min_delay_event", 10, 0, 20 * 60) diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index 5b6ab47..98a4e18 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -1400,6 +1400,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): ) ) elif coin == "namecoin": + fp.write("deprecatedrpc=create_bdb\n") fp.write("prune=2000\n") elif coin == "pivx": params_dir = os.path.join(data_dir, "pivx-params") @@ -1910,7 +1911,7 @@ def initialise_wallets( f'Creating wallet "{wallet_name}" for {getCoinName(c)}.' ) - if c in (Coins.BTC, Coins.LTC, Coins.DOGE, Coins.DASH): + if c in (Coins.BTC, Coins.LTC, Coins.NMC, Coins.DOGE, Coins.DASH): # wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors use_descriptors = coin_settings.get("use_descriptors", False) diff --git a/basicswap/config.py b/basicswap/config.py index 9c8d06d..cc010a0 100644 --- a/basicswap/config.py +++ b/basicswap/config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2019-2024 The Basicswap developers +# Copyright (c) 2019-2025 The Basicswap developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -40,13 +40,6 @@ DOGECOIND = os.getenv("DOGECOIND", "dogecoind" + bin_suffix) DOGECOIN_CLI = os.getenv("DOGECOIN_CLI", "dogecoin-cli" + bin_suffix) DOGECOIN_TX = os.getenv("DOGECOIN_TX", "dogecoin-tx" + bin_suffix) -NAMECOIN_BINDIR = os.path.expanduser( - os.getenv("NAMECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "namecoin")) -) -NAMECOIND = os.getenv("NAMECOIND", "namecoind" + bin_suffix) -NAMECOIN_CLI = os.getenv("NAMECOIN_CLI", "namecoin-cli" + bin_suffix) -NAMECOIN_TX = os.getenv("NAMECOIN_TX", "namecoin-tx" + bin_suffix) - XMR_BINDIR = os.path.expanduser( os.getenv("XMR_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "monero")) ) diff --git a/basicswap/pgp/keys/namecoin_JeremyRand.pgp b/basicswap/pgp/keys/namecoin_JeremyRand.pgp deleted file mode 100644 index 0e0ce2a..0000000 --- a/basicswap/pgp/keys/namecoin_JeremyRand.pgp +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEXPNfCBYJKwYBBAHaRw8BAQdAWGFiEJYnlV2TDTesLIO/eoQ3IPduzcG97GpA -6K+Gj+K0K0BKZXJlbXlSYW5kIG9uIEdpdEh1YiA8amVyZW15QG5hbWVjb2luLm9y -Zz6IlgQTFggAPhYhBJza8EpykDv+wJWdvi2+M54p9ilMBQJc88q7AhsDBQkB4TOA -BQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEC2+M54p9ilMMUoA/1oZn8AtwQ7D -wXgNKq++zqHaiEcHGgsyFeDRbQARsYRVAQDxa36p181id1YuMjeV1KhC5vaDS4nY -GB4FHPsQ4bbqDLRESmVyZW15IFJhbmQgKE5hbWVjb2luIENvcmUgR2l0aWFuIFNp -Z25pbmcgS2V5KSA8amVyZW15QG5hbWVjb2luLm9yZz6ImQQTFggAQQIbAwUJAeEz -gAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgBYhBJza8EpykDv+wJWdvi2+M54p9ilM -BQJc9WDrAhkBAAoJEC2+M54p9ilMz3IA/3mCKeFYcEJFlwP43mdIMGOV2zt/R4Fn -z/rBJpv5hFoHAQDXAY8+mbY/9N+5Rn8Iy51tXEaTq3khdruuFFdty+bXAg== -=EpnJ ------END PGP PUBLIC KEY BLOCK----- diff --git a/basicswap/pgp/keys/namecoin_RoseTuring.pgp b/basicswap/pgp/keys/namecoin_RoseTuring.pgp new file mode 100644 index 0000000..97c8076 --- /dev/null +++ b/basicswap/pgp/keys/namecoin_RoseTuring.pgp @@ -0,0 +1,52 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=atlH +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py index 80c29bf..1bee5a1 100644 --- a/tests/basicswap/extended/test_nmc.py +++ b/tests/basicswap/extended/test_nmc.py @@ -44,6 +44,7 @@ from basicswap.rpc import ( from basicswap.contrib.key import ( ECKey, ) +from basicswap.contrib.rpcauth import generate_salt, password_to_hmac from basicswap.http_server import ( HttpThread, ) @@ -64,6 +65,7 @@ from tests.basicswap.common import ( BASE_ZMQ_PORT, PREFIX_SECRET_KEY_REGTEST, waitForRPC, + make_rpc_func, ) from basicswap.bin.run import startDaemon @@ -73,6 +75,17 @@ logger.level = logging.DEBUG if not len(logger.handlers): logger.addHandler(logging.StreamHandler(sys.stdout)) + +NAMECOIN_BINDIR = os.path.expanduser( + os.getenv("NAMECOIN_BINDIR", os.path.join(cfg.DEFAULT_TEST_BINDIR, "namecoin")) +) +NAMECOIND = os.getenv("NAMECOIND", "namecoind" + cfg.bin_suffix) +NAMECOIN_CLI = os.getenv("NAMECOIN_CLI", "namecoin-cli" + cfg.bin_suffix) +NAMECOIN_TX = os.getenv("NAMECOIN_TX", "namecoin-tx" + cfg.bin_suffix) + +NMC_BASE_PORT = 8136 +NMC_BASE_RPC_PORT= 8146 + NUM_NODES = 3 NMC_NODE = 3 BTC_NODE = 4 @@ -90,8 +103,21 @@ def prepareOtherDir(datadir, nodeId, conf_file="namecoin.conf"): with open(filePath, "w+") as fp: fp.write("regtest=1\n") fp.write("[regtest]\n") - fp.write("port=" + str(BASE_PORT + nodeId) + "\n") - fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n") + + if conf_file == "bitcoin.conf": + fp.write("port=" + str(BASE_PORT + nodeId) + "\n") + fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n") + else: + fp.write("port=" + str(NMC_BASE_PORT + nodeId) + "\n") + fp.write("rpcport=" + str(NMC_BASE_RPC_PORT + nodeId) + "\n") + salt = generate_salt(16) + fp.write( + "rpcauth={}:{}${}\n".format( + "test" + str(nodeId), + salt, + password_to_hmac(salt, "test_pass" + str(nodeId)), + ) + ) fp.write("daemon=0\n") fp.write("printtoconsole=0\n") @@ -105,9 +131,8 @@ def prepareOtherDir(datadir, nodeId, conf_file="namecoin.conf"): fp.write("fallbackfee=0.01\n") fp.write("acceptnonstdtxn=0\n") - - if conf_file == "bitcoin.conf": - fp.write("wallet=wallet.dat\n") + fp.write("deprecatedrpc=create_bdb\n") + fp.write("wallet=wallet.dat\n") def prepareDir(datadir, nodeId, network_key, network_pubkey): @@ -175,11 +200,11 @@ def prepareDir(datadir, nodeId, network_key, network_pubkey): "namecoin": { "connection_type": "rpc", "manage_daemon": False, - "rpcport": BASE_RPC_PORT + NMC_NODE, + "rpcport": NMC_BASE_RPC_PORT + NMC_NODE, "datadir": nmcdatadir, - "bindir": cfg.NAMECOIN_BINDIR, - "use_csv": False, - # 'use_segwit': True, + "bindir": NAMECOIN_BINDIR, + "use_csv": True, + "use_segwit": True, }, "bitcoin": { "connection_type": "rpc", @@ -221,11 +246,11 @@ def btcRpc(cmd): def nmcRpc(cmd): return callrpc_cli( - cfg.NAMECOIN_BINDIR, + NAMECOIN_BINDIR, os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), "regtest", cmd, - cfg.NAMECOIN_CLI, + NAMECOIN_CLI, ) @@ -305,11 +330,15 @@ class Test(unittest.TestCase): cls.daemons.append( startDaemon( os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), - cfg.NAMECOIN_BINDIR, - cfg.NAMECOIND, + NAMECOIN_BINDIR, + NAMECOIND, ) ) - logging.info("Started %s %d", cfg.NAMECOIND, cls.daemons[-1].handle.pid) + logging.info("Started %s %d", NAMECOIND, cls.daemons[-1].handle.pid) + nmcRpc2 = make_rpc_func(NMC_NODE, base_rpc_port=NMC_BASE_RPC_PORT) + waitForRPC(nmcRpc2, delay_event, rpc_command="getblockchaininfo") + if len(nmcRpc2("listwallets")) < 1: + nmcRpc2("createwallet", ["wallet.dat", False, False, "", False, False]) for i in range(NUM_NODES): data_dir = os.path.join(cfg.TEST_DATADIRS, str(i)) @@ -378,10 +407,9 @@ class Test(unittest.TestCase): cls.http_threads.append(t) t.start() - waitForRPC(nmcRpc, delay_event) num_blocks = 500 logging.info("Mining %d namecoin blocks", num_blocks) - cls.nmc_addr = nmcRpc("getnewaddress mining_addr legacy") + cls.nmc_addr = nmcRpc("getnewaddress mining_addr bech32") nmcRpc("generatetoaddress {} {}".format(num_blocks, cls.nmc_addr)) ro = nmcRpc("getblockchaininfo") @@ -453,8 +481,7 @@ class Test(unittest.TestCase): 100 * COIN, 0.1 * COIN, 100 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_TIME, + SwapTypes.SELLER_FIRST ) wait_for_offer(delay_event, swap_clients[1], offer_id) @@ -494,8 +521,7 @@ class Test(unittest.TestCase): 10 * COIN, 9.0 * COIN, 10 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_TIME, + SwapTypes.SELLER_FIRST ) wait_for_offer(delay_event, swap_clients[0], offer_id) @@ -534,8 +560,7 @@ class Test(unittest.TestCase): 10 * COIN, 0.1 * COIN, 10 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_TIME, + SwapTypes.SELLER_FIRST ) wait_for_offer(delay_event, swap_clients[1], offer_id) @@ -577,7 +602,7 @@ class Test(unittest.TestCase): 0.1 * COIN, 10 * COIN, SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_BLOCKS, + TxLockTypes.SEQUENCE_LOCK_BLOCKS, 10, ) @@ -618,8 +643,7 @@ class Test(unittest.TestCase): 10 * COIN, 10 * COIN, 10 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_TIME, + SwapTypes.SELLER_FIRST ) wait_for_offer(delay_event, swap_clients[0], offer_id) @@ -655,11 +679,10 @@ class Test(unittest.TestCase): offer_id = swap_clients[0].postOffer( Coins.NMC, Coins.BTC, - 0.001 * COIN, + 0.01 * COIN, 1.0 * COIN, - 0.001 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.ABS_LOCK_TIME, + 0.01 * COIN, + SwapTypes.SELLER_FIRST ) wait_for_offer(delay_event, swap_clients[0], offer_id) @@ -669,8 +692,8 @@ class Test(unittest.TestCase): wait_for_bid(delay_event, swap_clients[0], bid_id) swap_clients[0].acceptBid(bid_id) try: - swap_clients[0].getChainClientSettings(Coins.BTC)["override_feerate"] = 10.0 - swap_clients[0].getChainClientSettings(Coins.NMC)["override_feerate"] = 10.0 + swap_clients[0].getChainClientSettings(Coins.BTC)["override_feerate"] = 100.0 + swap_clients[0].getChainClientSettings(Coins.NMC)["override_feerate"] = 100.0 wait_for_bid( delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60 ) From f263bb53c31e393b40cba6626aa86ae2c727c595 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Fri, 28 Mar 2025 22:31:50 +0200 Subject: [PATCH 4/8] nmc: Update test. --- basicswap/bin/prepare.py | 82 +-- basicswap/chainparams.py | 6 + basicswap/interface/nmc.py | 87 +-- tests/basicswap/extended/test_dcr.py | 38 +- tests/basicswap/extended/test_nmc.py | 779 ++++++--------------------- tests/basicswap/test_btc_xmr.py | 58 +- 6 files changed, 309 insertions(+), 741 deletions(-) diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index 98a4e18..233e30f 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -52,11 +52,17 @@ PARTICL_VERSION = os.getenv("PARTICL_VERSION", "23.2.7.0") PARTICL_VERSION_TAG = os.getenv("PARTICL_VERSION_TAG", "") PARTICL_LINUX_EXTRA = os.getenv("PARTICL_LINUX_EXTRA", "nousb") +BITCOIN_VERSION = os.getenv("BITCOIN_VERSION", "28.0") +BITCOIN_VERSION_TAG = os.getenv("BITCOIN_VERSION_TAG", "") + LITECOIN_VERSION = os.getenv("LITECOIN_VERSION", "0.21.4") LITECOIN_VERSION_TAG = os.getenv("LITECOIN_VERSION_TAG", "") -BITCOIN_VERSION = os.getenv("BITCOIN_VERSION", "28.0") -BITCOIN_VERSION_TAG = os.getenv("BITCOIN_VERSION_TAG", "") +DCR_VERSION = os.getenv("DCR_VERSION", "1.8.1") +DCR_VERSION_TAG = os.getenv("DCR_VERSION_TAG", "") + +NMC_VERSION = os.getenv("NMC_VERSION", "28.0") +NMC_VERSION_TAG = os.getenv("NMC_VERSION_TAG", "") MONERO_VERSION = os.getenv("MONERO_VERSION", "0.18.3.4") MONERO_VERSION_TAG = os.getenv("MONERO_VERSION_TAG", "") @@ -82,12 +88,6 @@ FIRO_VERSION_TAG = os.getenv("FIRO_VERSION_TAG", "") NAV_VERSION = os.getenv("NAV_VERSION", "7.0.3") NAV_VERSION_TAG = os.getenv("NAV_VERSION_TAG", "") -NMC_VERSION = os.getenv("NAV_VERSION", "28.0") -NMC_VERSION_TAG = os.getenv("NAV_VERSION_TAG", "") - -DCR_VERSION = os.getenv("DCR_VERSION", "1.8.1") -DCR_VERSION_TAG = os.getenv("DCR_VERSION_TAG", "") - BITCOINCASH_VERSION = os.getenv("BITCOINCASH_VERSION", "28.0.1") BITCOINCASH_VERSION_TAG = os.getenv("BITCOINCASH_VERSION_TAG", "") @@ -117,9 +117,7 @@ known_coins = { "dogecoin": (DOGECOIN_VERSION, DOGECOIN_VERSION_TAG, ("tecnovert",)), } -disabled_coins = [ - "navcoin" -] +disabled_coins = ["navcoin"] expected_key_ids = { "tecnovert": ("13F13651C9CF0D6B",), @@ -184,6 +182,35 @@ PART_ONION_PORT = int(os.getenv("PART_ONION_PORT", 51734)) PART_RPC_USER = os.getenv("PART_RPC_USER", "") PART_RPC_PWD = os.getenv("PART_RPC_PWD", "") +BTC_RPC_HOST = os.getenv("BTC_RPC_HOST", "127.0.0.1") +BTC_RPC_PORT = int(os.getenv("BTC_RPC_PORT", 19996)) +BTC_PORT = int(os.getenv("BTC_PORT", 8333)) +BTC_ONION_PORT = int(os.getenv("BTC_ONION_PORT", 8334)) +BTC_RPC_USER = os.getenv("BTC_RPC_USER", "") +BTC_RPC_PWD = os.getenv("BTC_RPC_PWD", "") + +LTC_RPC_HOST = os.getenv("LTC_RPC_HOST", "127.0.0.1") +LTC_RPC_PORT = int(os.getenv("LTC_RPC_PORT", 19895)) +LTC_ONION_PORT = int(os.getenv("LTC_ONION_PORT", 9333)) +LTC_RPC_USER = os.getenv("LTC_RPC_USER", "") +LTC_RPC_PWD = os.getenv("LTC_RPC_PWD", "") + +DCR_RPC_HOST = os.getenv("DCR_RPC_HOST", "127.0.0.1") +DCR_RPC_PORT = int(os.getenv("DCR_RPC_PORT", 9109)) +DCR_WALLET_RPC_HOST = os.getenv("DCR_WALLET_RPC_HOST", "127.0.0.1") +DCR_WALLET_RPC_PORT = int(os.getenv("DCR_WALLET_RPC_PORT", 9209)) +DCR_WALLET_PWD = os.getenv( + "DCR_WALLET_PWD", random.randbytes(random.randint(14, 18)).hex() +) +DCR_RPC_USER = os.getenv("DCR_RPC_USER", "user") +DCR_RPC_PWD = os.getenv("DCR_RPC_PWD", random.randbytes(random.randint(14, 18)).hex()) + +NMC_RPC_HOST = os.getenv("NMC_RPC_HOST", "127.0.0.1") +NMC_RPC_PORT = int(os.getenv("NMC_RPC_PORT", 19698)) +NMC_ONION_PORT = int(os.getenv("NMC_ONION_PORT", 9698)) +NMC_RPC_USER = os.getenv("NMC_RPC_USER", "") +NMC_RPC_PWD = os.getenv("NMC_RPC_PWD", "") + XMR_RPC_HOST = os.getenv("XMR_RPC_HOST", "127.0.0.1") XMR_RPC_PORT = int(os.getenv("XMR_RPC_PORT", 29798)) XMR_ZMQ_PORT = int(os.getenv("XMR_ZMQ_PORT", 30898)) @@ -206,35 +233,6 @@ WOW_RPC_USER = os.getenv("WOW_RPC_USER", "") WOW_RPC_PWD = os.getenv("WOW_RPC_PWD", "") DEFAULT_WOW_RESTORE_HEIGHT = int(os.getenv("DEFAULT_WOW_RESTORE_HEIGHT", 450000)) -LTC_RPC_HOST = os.getenv("LTC_RPC_HOST", "127.0.0.1") -LTC_RPC_PORT = int(os.getenv("LTC_RPC_PORT", 19895)) -LTC_ONION_PORT = int(os.getenv("LTC_ONION_PORT", 9333)) -LTC_RPC_USER = os.getenv("LTC_RPC_USER", "") -LTC_RPC_PWD = os.getenv("LTC_RPC_PWD", "") - -BTC_RPC_HOST = os.getenv("BTC_RPC_HOST", "127.0.0.1") -BTC_RPC_PORT = int(os.getenv("BTC_RPC_PORT", 19996)) -BTC_PORT = int(os.getenv("BTC_PORT", 8333)) -BTC_ONION_PORT = int(os.getenv("BTC_ONION_PORT", 8334)) -BTC_RPC_USER = os.getenv("BTC_RPC_USER", "") -BTC_RPC_PWD = os.getenv("BTC_RPC_PWD", "") - -DCR_RPC_HOST = os.getenv("DCR_RPC_HOST", "127.0.0.1") -DCR_RPC_PORT = int(os.getenv("DCR_RPC_PORT", 9109)) -DCR_WALLET_RPC_HOST = os.getenv("DCR_WALLET_RPC_HOST", "127.0.0.1") -DCR_WALLET_RPC_PORT = int(os.getenv("DCR_WALLET_RPC_PORT", 9209)) -DCR_WALLET_PWD = os.getenv( - "DCR_WALLET_PWD", random.randbytes(random.randint(14, 18)).hex() -) -DCR_RPC_USER = os.getenv("DCR_RPC_USER", "user") -DCR_RPC_PWD = os.getenv("DCR_RPC_PWD", random.randbytes(random.randint(14, 18)).hex()) - -NMC_RPC_HOST = os.getenv("NMC_RPC_HOST", "127.0.0.1") -NMC_RPC_PORT = int(os.getenv("NMC_RPC_PORT", 19698)) -NMC_ONION_PORT = int(os.getenv("NMC_ONION_PORT", 9698)) -NMC_RPC_USER = os.getenv("NMC_RPC_USER", "") -NMC_RPC_PWD = os.getenv("NMC_RPC_PWD", "") - PIVX_RPC_HOST = os.getenv("PIVX_RPC_HOST", "127.0.0.1") PIVX_RPC_PORT = int(os.getenv("PIVX_RPC_PORT", 51473)) PIVX_ONION_PORT = int(os.getenv("PIVX_ONION_PORT", 51472)) # nDefaultPort @@ -1400,8 +1398,10 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): ) ) elif coin == "namecoin": - fp.write("deprecatedrpc=create_bdb\n") fp.write("prune=2000\n") + fp.write("deprecatedrpc=create_bdb\n") + fp.write("addresstype=bech32\n") + fp.write("changetype=bech32\n") elif coin == "pivx": params_dir = os.path.join(data_dir, "pivx-params") downloadPIVXParams(params_dir) diff --git a/basicswap/chainparams.py b/basicswap/chainparams.py index 82d7ad4..f4e36d5 100644 --- a/basicswap/chainparams.py +++ b/basicswap/chainparams.py @@ -256,6 +256,8 @@ chainparams = { "bip44": 7, "min_amount": 100000, "max_amount": 10000000 * COIN, + "ext_public_key_prefix": 0x0488B21E, # base58Prefixes[EXT_PUBLIC_KEY] + "ext_secret_key_prefix": 0x0488ADE4, }, "testnet": { "rpcport": 18336, @@ -267,6 +269,8 @@ chainparams = { "min_amount": 100000, "max_amount": 10000000 * COIN, "name": "testnet3", + "ext_public_key_prefix": 0x043587CF, + "ext_secret_key_prefix": 0x04358394, }, "regtest": { "rpcport": 18443, @@ -277,6 +281,8 @@ chainparams = { "bip44": 1, "min_amount": 100000, "max_amount": 10000000 * COIN, + "ext_public_key_prefix": 0x043587CF, + "ext_secret_key_prefix": 0x04358394, }, }, Coins.XMR: { diff --git a/basicswap/interface/nmc.py b/basicswap/interface/nmc.py index cf9bae7..19aa48e 100644 --- a/basicswap/interface/nmc.py +++ b/basicswap/interface/nmc.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020-2022 tecnovert +# Copyright (c) 2025 The Basicswap developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -14,38 +15,56 @@ class NMCInterface(BTCInterface): def coin_type(): return Coins.NMC - def getLockTxHeight( - self, - txid, - dest_address, - bid_amount, - rescan_from, - find_index: bool = False, - vout: int = -1, - ): - self._log.debug("[rm] scantxoutset start") # scantxoutset is slow - ro = self.rpc( - "scantxoutset", ["start", ["addr({})".format(dest_address)]] - ) # TODO: Use combo(address) where possible - self._log.debug("[rm] scantxoutset end") - return_txid = True if txid is None else False - for o in ro["unspents"]: - if txid and o["txid"] != txid.hex(): - continue - # Verify amount - if self.make_int(o["amount"]) != int(bid_amount): - self._log.warning( - "Found output to lock tx address of incorrect value: %s, %s", - str(o["amount"]), - o["txid"], - ) - continue + def lockNonSegwitPrevouts(self) -> None: + # For tests + # NMC Seems to ignore utxo locks + unspent = self.rpc_wallet("listunspent") - rv = {"depth": 0, "height": o["height"]} - if o["height"] > 0: - rv["depth"] = ro["height"] - o["height"] - if find_index: - rv["index"] = o["vout"] - if return_txid: - rv["txid"] = o["txid"] - return rv + to_lock = [] + for u in unspent: + if u.get("spendable", False) is False: + continue + if "desc" in u: + desc = u["desc"] + if self.use_p2shp2wsh(): + if not desc.startswith("sh(wpkh"): + to_lock.append( + { + "txid": u["txid"], + "vout": u["vout"], + "amount": u["amount"], + } + ) + else: + if not desc.startswith("wpkh"): + to_lock.append( + { + "txid": u["txid"], + "vout": u["vout"], + "amount": u["amount"], + } + ) + + if len(to_lock) > 0: + self._log.debug(f"Spending {len(to_lock)} non segwit prevouts") + addr_out = self.rpc_wallet( + "getnewaddress", ["convert non segwit", "bech32"] + ) + prevouts = [] + sum_amount: int = 0 + for utxo in to_lock: + prevouts.append( + { + "txid": utxo["txid"], + "vout": utxo["vout"], + } + ) + sum_amount += self.make_int(utxo["amount"]) + + fee = 100000 * len(prevouts) + funded_tx = self.rpc( + "createrawtransaction", + [prevouts, {addr_out: self.format_amount(sum_amount - fee)}], + ) + signed_tx = self.rpc_wallet("signrawtransactionwithwallet", [funded_tx]) + self.rpc("sendrawtransaction", [signed_tx["hex"]]) diff --git a/tests/basicswap/extended/test_dcr.py b/tests/basicswap/extended/test_dcr.py index d063160..e3c7bfe 100644 --- a/tests/basicswap/extended/test_dcr.py +++ b/tests/basicswap/extended/test_dcr.py @@ -138,7 +138,6 @@ def run_test_success_path(self, coin_from: Coins, coin_to: Coins): # Verify lock tx spends are found in the expected wallets bid, offer = swap_clients[node_from].getBidAndOffer(bid_id) - max_fee: int = 10000 itx_spend = bid.initiate_tx.spend_txid.hex() node_to_ci_from = swap_clients[node_to].ci(coin_from) wtx = node_to_ci_from.rpc_wallet( @@ -147,7 +146,9 @@ def run_test_success_path(self, coin_from: Coins, coin_to: Coins): itx_spend, ], ) - assert amt_swap - node_to_ci_from.make_int(wtx["details"][0]["amount"]) < max_fee + assert ( + amt_swap - node_to_ci_from.make_int(wtx["details"][0]["amount"]) < self.max_fee + ) node_from_ci_to = swap_clients[node_from].ci(coin_to) ptx_spend = bid.participate_tx.spend_txid.hex() @@ -158,7 +159,8 @@ def run_test_success_path(self, coin_from: Coins, coin_to: Coins): ], ) assert ( - bid.amount_to - node_from_ci_to.make_int(wtx["details"][0]["amount"]) < max_fee + bid.amount_to - node_from_ci_to.make_int(wtx["details"][0]["amount"]) + < self.max_fee ) js_0 = read_json_api(1800 + node_from) @@ -235,7 +237,6 @@ def run_test_bad_ptx(self, coin_from: Coins, coin_to: Coins): # Verify lock tx spends are found in the expected wallets bid, offer = swap_clients[node_from].getBidAndOffer(bid_id) - max_fee: int = 10000 itx_spend = bid.initiate_tx.spend_txid.hex() node_from_ci_from = swap_clients[node_from].ci(coin_from) wtx = node_from_ci_from.rpc_wallet( @@ -244,7 +245,10 @@ def run_test_bad_ptx(self, coin_from: Coins, coin_to: Coins): itx_spend, ], ) - assert amt_swap - node_from_ci_from.make_int(wtx["details"][0]["amount"]) < max_fee + assert ( + amt_swap - node_from_ci_from.make_int(wtx["details"][0]["amount"]) + < self.max_fee + ) node_to_ci_to = swap_clients[node_to].ci(coin_to) bid, offer = swap_clients[node_to].getBidAndOffer(bid_id) @@ -255,7 +259,10 @@ def run_test_bad_ptx(self, coin_from: Coins, coin_to: Coins): ptx_spend, ], ) - assert bid.amount_to - node_to_ci_to.make_int(wtx["details"][0]["amount"]) < max_fee + assert ( + bid.amount_to - node_to_ci_to.make_int(wtx["details"][0]["amount"]) + < self.max_fee + ) bid_id_hex = bid_id.hex() path = f"bids/{bid_id_hex}/states" @@ -338,7 +345,6 @@ def run_test_itx_refund(self, coin_from: Coins, coin_to: Coins): # Verify lock tx spends are found in the expected wallets bid, offer = swap_clients[node_from].getBidAndOffer(bid_id) - max_fee: int = 10000 itx_spend = bid.initiate_tx.spend_txid.hex() node_from_ci_from = swap_clients[node_from].ci(coin_from) wtx = node_from_ci_from.rpc_wallet( @@ -348,7 +354,8 @@ def run_test_itx_refund(self, coin_from: Coins, coin_to: Coins): ], ) assert ( - swap_value - node_from_ci_from.make_int(wtx["details"][0]["amount"]) < max_fee + swap_value - node_from_ci_from.make_int(wtx["details"][0]["amount"]) + < self.max_fee ) node_from_ci_to = swap_clients[node_from].ci(coin_to) @@ -360,7 +367,8 @@ def run_test_itx_refund(self, coin_from: Coins, coin_to: Coins): ], ) assert ( - bid.amount_to - node_from_ci_to.make_int(wtx["details"][0]["amount"]) < max_fee + bid.amount_to - node_from_ci_to.make_int(wtx["details"][0]["amount"]) + < self.max_fee ) @@ -425,7 +433,6 @@ def run_test_ads_success_path(self, coin_from: Coins, coin_to: Coins): bid, xmr_swap = swap_clients[id_offerer].getXmrBid(bid_id) node_from_ci_to = swap_clients[0].ci(coin_to) - max_fee: int = 10000 if node_from_ci_to.coin_type() in (Coins.XMR, Coins.WOW): pass else: @@ -437,7 +444,7 @@ def run_test_ads_success_path(self, coin_from: Coins, coin_to: Coins): ) assert ( bid.amount_to - node_from_ci_to.make_int(wtx["details"][0]["amount"]) - < max_fee + < self.max_fee ) node_to_ci_from = swap_clients[1].ci(coin_from) @@ -451,7 +458,8 @@ def run_test_ads_success_path(self, coin_from: Coins, coin_to: Coins): ], ) assert ( - bid.amount - node_to_ci_from.make_int(wtx["details"][0]["amount"]) < max_fee + bid.amount - node_to_ci_from.make_int(wtx["details"][0]["amount"]) + < self.max_fee ) bid_id_hex = bid_id.hex() @@ -550,7 +558,6 @@ def run_test_ads_both_refund( bid, xmr_swap = swap_clients[id_bidder].getXmrBid(bid_id) node_from_ci_from = swap_clients[0].ci(coin_from) - max_fee: int = 10000 if node_from_ci_from.coin_type() in (Coins.XMR, Coins.WOW): pass else: @@ -562,7 +569,7 @@ def run_test_ads_both_refund( ) assert ( bid.amount - node_from_ci_from.make_int(wtx["details"][0]["amount"]) - < max_fee + < self.max_fee ) node_to_ci_to = swap_clients[1].ci(coin_to) @@ -577,7 +584,7 @@ def run_test_ads_both_refund( ) assert ( bid.amount_to - node_to_ci_to.make_int(wtx["details"][0]["amount"]) - < max_fee + < self.max_fee ) bid_id_hex = bid_id.hex() @@ -720,6 +727,7 @@ class Test(BaseTest): start_xmr_nodes = True dcr_mining_addr = "SsYbXyjkKAEXXcGdFgr4u4bo4L8RkCxwQpH" extra_wait_time = 0 + max_fee: int = 10000 hex_seeds = [ "e8574b2a94404ee62d8acc0258cab4c0defcfab8a5dfc2f4954c1f9d7e09d72a", diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py index 1bee5a1..ac839e9 100644 --- a/tests/basicswap/extended/test_nmc.py +++ b/tests/basicswap/extended/test_nmc.py @@ -11,67 +11,38 @@ basicswap]$ python tests/basicswap/extended/test_nmc.py """ -import json import logging import os -import shutil -import signal +import random import sys -import threading -import time import unittest import basicswap.config as cfg from basicswap.basicswap import ( - BasicSwap, Coins, - SwapTypes, - BidStates, - TxStates, ) from basicswap.util import ( - COIN, -) -from basicswap.basicswap_util import ( - TxLockTypes, -) -from basicswap.util.address import ( - toWIF, -) -from basicswap.rpc import ( - callrpc_cli, -) -from basicswap.contrib.key import ( - ECKey, + toBool, ) +from basicswap.bin.run import startDaemon from basicswap.contrib.rpcauth import generate_salt, password_to_hmac -from basicswap.http_server import ( - HttpThread, -) -from tests.basicswap.util import ( - read_json_api, -) from tests.basicswap.common import ( - checkForks, stopDaemons, - wait_for_offer, - wait_for_bid, - wait_for_bid_tx_state, - wait_for_in_progress, - TEST_HTTP_HOST, - TEST_HTTP_PORT, - BASE_PORT, - BASE_RPC_PORT, - BASE_ZMQ_PORT, - PREFIX_SECRET_KEY_REGTEST, waitForRPC, make_rpc_func, ) -from basicswap.bin.run import startDaemon + +from tests.basicswap.test_btc_xmr import BasicSwapTest, test_delay_event +from tests.basicswap.test_xmr import NUM_NODES +from tests.basicswap.extended.test_dcr import ( + run_test_success_path, + run_test_bad_ptx, + run_test_itx_refund, +) -logger = logging.getLogger() -logger.level = logging.DEBUG +logger = logging.getLogger("BSX Tests") + if not len(logger.handlers): logger.addHandler(logging.StreamHandler(sys.stdout)) @@ -83,19 +54,14 @@ NAMECOIND = os.getenv("NAMECOIND", "namecoind" + cfg.bin_suffix) NAMECOIN_CLI = os.getenv("NAMECOIN_CLI", "namecoin-cli" + cfg.bin_suffix) NAMECOIN_TX = os.getenv("NAMECOIN_TX", "namecoin-tx" + cfg.bin_suffix) +USE_DESCRIPTOR_WALLETS = toBool(os.getenv("USE_DESCRIPTOR_WALLETS", False)) + NMC_BASE_PORT = 8136 -NMC_BASE_RPC_PORT= 8146 - -NUM_NODES = 3 -NMC_NODE = 3 -BTC_NODE = 4 - -delay_event = threading.Event() -stop_test = False +NMC_BASE_RPC_PORT = 8146 -def prepareOtherDir(datadir, nodeId, conf_file="namecoin.conf"): - node_dir = os.path.join(datadir, str(nodeId)) +def prepareNMCDataDir(datadir, nodeId, conf_file="namecoin.conf"): + node_dir = os.path.join(datadir, "nmc_" + str(nodeId)) if not os.path.exists(node_dir): os.makedirs(node_dir) filePath = os.path.join(node_dir, conf_file) @@ -104,20 +70,16 @@ def prepareOtherDir(datadir, nodeId, conf_file="namecoin.conf"): fp.write("regtest=1\n") fp.write("[regtest]\n") - if conf_file == "bitcoin.conf": - fp.write("port=" + str(BASE_PORT + nodeId) + "\n") - fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n") - else: - fp.write("port=" + str(NMC_BASE_PORT + nodeId) + "\n") - fp.write("rpcport=" + str(NMC_BASE_RPC_PORT + nodeId) + "\n") - salt = generate_salt(16) - fp.write( - "rpcauth={}:{}${}\n".format( - "test" + str(nodeId), - salt, - password_to_hmac(salt, "test_pass" + str(nodeId)), - ) + fp.write("port=" + str(NMC_BASE_PORT + nodeId) + "\n") + fp.write("rpcport=" + str(NMC_BASE_RPC_PORT + nodeId) + "\n") + salt = generate_salt(16) + fp.write( + "rpcauth={}:{}${}\n".format( + "test" + str(nodeId), + salt, + password_to_hmac(salt, "test_pass" + str(nodeId)), ) + ) fp.write("daemon=0\n") fp.write("printtoconsole=0\n") @@ -125,592 +87,171 @@ def prepareOtherDir(datadir, nodeId, conf_file="namecoin.conf"): fp.write("discover=0\n") fp.write("listenonion=0\n") fp.write("bind=127.0.0.1\n") - fp.write("findpeers=0\n") fp.write("debug=1\n") fp.write("debugexclude=libevent\n") fp.write("fallbackfee=0.01\n") fp.write("acceptnonstdtxn=0\n") fp.write("deprecatedrpc=create_bdb\n") - fp.write("wallet=wallet.dat\n") - - -def prepareDir(datadir, nodeId, network_key, network_pubkey): - node_dir = os.path.join(datadir, str(nodeId)) - if not os.path.exists(node_dir): - os.makedirs(node_dir) - filePath = os.path.join(node_dir, "particl.conf") - - with open(filePath, "w+") as fp: - fp.write("regtest=1\n") - fp.write("[regtest]\n") - fp.write("port=" + str(BASE_PORT + nodeId) + "\n") - fp.write("rpcport=" + str(BASE_RPC_PORT + nodeId) + "\n") - - fp.write("daemon=0\n") - fp.write("printtoconsole=0\n") - fp.write("server=1\n") - fp.write("discover=0\n") - fp.write("listenonion=0\n") - fp.write("bind=127.0.0.1\n") - fp.write("findpeers=0\n") - fp.write("debug=1\n") - fp.write("debugexclude=libevent\n") - fp.write("zmqpubsmsg=tcp://127.0.0.1:" + str(BASE_ZMQ_PORT + nodeId) + "\n") - fp.write("wallet=wallet.dat\n") - fp.write("fallbackfee=0.01\n") - - fp.write("acceptnonstdtxn=0\n") - fp.write("minstakeinterval=5\n") - fp.write("smsgsregtestadjust=0\n") + fp.write("addresstype=bech32\n") + fp.write("changetype=bech32\n") for i in range(0, NUM_NODES): if nodeId == i: continue - fp.write("addnode=127.0.0.1:%d\n" % (BASE_PORT + i)) - - if nodeId < 2: - fp.write("spentindex=1\n") - fp.write("txindex=1\n") - - basicswap_dir = os.path.join(datadir, str(nodeId), "basicswap") - if not os.path.exists(basicswap_dir): - os.makedirs(basicswap_dir) - - nmcdatadir = os.path.join(datadir, str(NMC_NODE)) - btcdatadir = os.path.join(datadir, str(BTC_NODE)) - settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) - settings = { - "debug": True, - "zmqhost": "tcp://127.0.0.1", - "zmqport": BASE_ZMQ_PORT + nodeId, - "htmlhost": "127.0.0.1", - "htmlport": 12700 + nodeId, - "network_key": network_key, - "network_pubkey": network_pubkey, - "chainclients": { - "particl": { - "connection_type": "rpc", - "manage_daemon": False, - "rpcport": BASE_RPC_PORT + nodeId, - "datadir": node_dir, - "bindir": cfg.PARTICL_BINDIR, - "blocks_confirmed": 2, # Faster testing - }, - "namecoin": { - "connection_type": "rpc", - "manage_daemon": False, - "rpcport": NMC_BASE_RPC_PORT + NMC_NODE, - "datadir": nmcdatadir, - "bindir": NAMECOIN_BINDIR, - "use_csv": True, - "use_segwit": True, - }, - "bitcoin": { - "connection_type": "rpc", - "manage_daemon": False, - "rpcport": BASE_RPC_PORT + BTC_NODE, - "datadir": btcdatadir, - "bindir": cfg.BITCOIN_BINDIR, - "use_segwit": True, - }, - }, - "check_progress_seconds": 2, - "check_watched_seconds": 4, - "check_expired_seconds": 60, - "restrict_unknown_seed_wallets": False, - } - with open(settings_path, "w") as fp: - json.dump(settings, fp, indent=4) + fp.write("addnode=127.0.0.1:{}\n".format(NMC_BASE_PORT + i)) -def partRpc(cmd, node_id=0): - return callrpc_cli( - cfg.PARTICL_BINDIR, - os.path.join(cfg.TEST_DATADIRS, str(node_id)), - "regtest", - cmd, - cfg.PARTICL_CLI, - ) +class TestNMC(BasicSwapTest): + __test__ = True + test_coin = Coins.NMC + test_coin_from = Coins.NMC + nmc_daemons = [] + start_ltc_nodes = False + start_xmr_nodes = True + base_rpc_port = NMC_BASE_RPC_PORT + nmc_addr = None + max_fee: int = 200000 + test_fee_rate: int = 10000 # sats/kvB - -def btcRpc(cmd): - return callrpc_cli( - cfg.BITCOIN_BINDIR, - os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)), - "regtest", - cmd, - cfg.BITCOIN_CLI, - ) - - -def nmcRpc(cmd): - return callrpc_cli( - NAMECOIN_BINDIR, - os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), - "regtest", - cmd, - NAMECOIN_CLI, - ) - - -def signal_handler(sig, frame): - global stop_test - os.write(sys.stdout.fileno(), f"Signal {sig} detected.\n".encode("utf-8")) - stop_test = True - delay_event.set() - - -def run_coins_loop(cls): - while not stop_test: - try: - nmcRpc("generatetoaddress 1 {}".format(cls.nmc_addr)) - btcRpc("generatetoaddress 1 {}".format(cls.btc_addr)) - except Exception as e: - logging.warning("run_coins_loop " + str(e)) - time.sleep(1.0) - - -def run_loop(self): - while not stop_test: - for c in self.swap_clients: - c.update() - time.sleep(1) - - -def make_part_cli_rpc_func(node_id): - node_id = node_id - - def rpc_func(method, params=None, wallet=None): - cmd = method - if params: - for p in params: - cmd += ' "' + p + '"' - return partRpc(cmd, node_id) - - return rpc_func - - -class Test(unittest.TestCase): + def mineBlock(self, num_blocks: int = 1) -> None: + self.callnoderpc("generatetoaddress", [num_blocks, self.nmc_addr]) @classmethod - def setUpClass(cls): - super(Test, cls).setUpClass() + def tearDownClass(cls): + logging.info("Finalising Namecoin Test") + stopDaemons(cls.nmc_daemons) + cls.nmc_daemons.clear() - eckey = ECKey() - eckey.generate() - cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) - cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + super(TestNMC, cls).tearDownClass() - if os.path.isdir(cfg.TEST_DATADIRS): - logging.info("Removing " + cfg.TEST_DATADIRS) - shutil.rmtree(cfg.TEST_DATADIRS) + @classmethod + def coins_loop(cls): + super(TestNMC, cls).coins_loop() + ci0 = cls.swap_clients[0].ci(cls.test_coin) + try: + if cls.nmc_addr is not None: + ci0.rpc_wallet("generatetoaddress", [1, cls.nmc_addr]) + except Exception as e: + logging.warning(f"coins_loop generate {e}") - for i in range(NUM_NODES): - prepareDir(cfg.TEST_DATADIRS, i, cls.network_key, cls.network_pubkey) + @classmethod + def prepareExtraDataDir(cls, i: int) -> None: + if not cls.restore_instance: + prepareNMCDataDir(cfg.TEST_DATADIRS, i) - prepareOtherDir(cfg.TEST_DATADIRS, NMC_NODE) - prepareOtherDir(cfg.TEST_DATADIRS, BTC_NODE, "bitcoin.conf") - - cls.daemons = [] - cls.swap_clients = [] - cls.http_threads = [] - - btc_data_dir = os.path.join(cfg.TEST_DATADIRS, str(BTC_NODE)) - if os.path.exists(os.path.join(cfg.BITCOIN_BINDIR, "bitcoin-wallet")): - callrpc_cli( - cfg.BITCOIN_BINDIR, - btc_data_dir, - "regtest", - "-wallet=wallet.dat -legacy create", - "bitcoin-wallet", - ) - cls.daemons.append(startDaemon(btc_data_dir, cfg.BITCOIN_BINDIR, cfg.BITCOIND)) - logging.info("Started %s %d", cfg.BITCOIND, cls.daemons[-1].handle.pid) - cls.daemons.append( + cls.nmc_daemons.append( startDaemon( - os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)), + os.path.join(cfg.TEST_DATADIRS, "nmc_" + str(i)), NAMECOIN_BINDIR, NAMECOIND, ) ) - logging.info("Started %s %d", NAMECOIND, cls.daemons[-1].handle.pid) - nmcRpc2 = make_rpc_func(NMC_NODE, base_rpc_port=NMC_BASE_RPC_PORT) - waitForRPC(nmcRpc2, delay_event, rpc_command="getblockchaininfo") - if len(nmcRpc2("listwallets")) < 1: - nmcRpc2("createwallet", ["wallet.dat", False, False, "", False, False]) + logging.info("Started {} {}".format(NAMECOIND, cls.nmc_daemons[-1].handle.pid)) - for i in range(NUM_NODES): - data_dir = os.path.join(cfg.TEST_DATADIRS, str(i)) - if os.path.exists(os.path.join(cfg.PARTICL_BINDIR, "particl-wallet")): - callrpc_cli( - cfg.PARTICL_BINDIR, - data_dir, - "regtest", - "-wallet=wallet.dat -legacy create", - "particl-wallet", - ) - cls.daemons.append(startDaemon(data_dir, cfg.PARTICL_BINDIR, cfg.PARTICLD)) - logging.info("Started %s %d", cfg.PARTICLD, cls.daemons[-1].handle.pid) - - for i in range(NUM_NODES): - rpc = make_part_cli_rpc_func(i) - waitForRPC(rpc, delay_event) - if i == 0: - rpc( - "extkeyimportmaster", - [ - "abandon baby cabbage dad eager fabric gadget habit ice kangaroo lab absorb" - ], - ) - elif i == 1: - rpc( - "extkeyimportmaster", - [ - "pact mammal barrel matrix local final lecture chunk wasp survey bid various book strong spread fall ozone daring like topple door fatigue limb olympic", - "", - "true", - ], - ) - rpc("getnewextaddress", ["lblExtTest"]) - rpc("rescanblockchain") - else: - rpc("extkeyimportmaster", [rpc("mnemonic", ["new"])["master"]]) - rpc( - "walletsettings", - [ - "stakingoptions", - json.dumps( - {"stakecombinethreshold": 100, "stakesplitthreshold": 200} - ).replace('"', '\\"'), - ], + nmc_rpc = make_rpc_func(i, base_rpc_port=NMC_BASE_RPC_PORT) + waitForRPC( + nmc_rpc, + test_delay_event, + rpc_command="getnetworkinfo", + max_tries=12, + ) + waitForRPC(nmc_rpc, test_delay_event, rpc_command="getblockchaininfo") + if len(nmc_rpc("listwallets")) < 1: + nmc_rpc( + "createwallet", + ["wallet.dat", False, False, "", False, USE_DESCRIPTOR_WALLETS], ) - rpc("reservebalance", ["false"]) - - basicswap_dir = os.path.join( - os.path.join(cfg.TEST_DATADIRS, str(i)), "basicswap" - ) - settings_path = os.path.join(basicswap_dir, cfg.CONFIG_FILENAME) - with open(settings_path) as fs: - settings = json.load(fs) - sc = BasicSwap( - basicswap_dir, settings, "regtest", log_name="BasicSwap{}".format(i) - ) - cls.swap_clients.append(sc) - - sc.setDaemonPID(Coins.BTC, cls.daemons[0].handle.pid) - sc.setDaemonPID(Coins.NMC, cls.daemons[1].handle.pid) - sc.setDaemonPID(Coins.PART, cls.daemons[2 + i].handle.pid) - sc.start() - - t = HttpThread(TEST_HTTP_HOST, TEST_HTTP_PORT + i, False, sc) - cls.http_threads.append(t) - t.start() - - num_blocks = 500 - logging.info("Mining %d namecoin blocks", num_blocks) - cls.nmc_addr = nmcRpc("getnewaddress mining_addr bech32") - nmcRpc("generatetoaddress {} {}".format(num_blocks, cls.nmc_addr)) - - ro = nmcRpc("getblockchaininfo") - try: - assert ro["bip9_softforks"]["csv"]["status"] == "active" - except Exception: - logging.info("nmc: csv is not active") - try: - assert ro["bip9_softforks"]["segwit"]["status"] == "active" - except Exception: - logging.info("nmc: segwit is not active") - - waitForRPC(btcRpc, delay_event) - cls.btc_addr = btcRpc("getnewaddress mining_addr bech32") - logging.info("Mining %d Bitcoin blocks to %s", num_blocks, cls.btc_addr) - btcRpc("generatetoaddress {} {}".format(num_blocks, cls.btc_addr)) - - ro = btcRpc("getblockchaininfo") - checkForks(ro) - - ro = nmcRpc("getwalletinfo") - print("nmcRpc", ro) - - signal.signal(signal.SIGINT, signal_handler) - cls.update_thread = threading.Thread(target=run_loop, args=(cls,)) - cls.update_thread.start() - - cls.coins_update_thread = threading.Thread(target=run_coins_loop, args=(cls,)) - cls.coins_update_thread.start() - - # Wait for height, or sequencelock is thrown off by genesis blocktime - num_blocks = 3 - logging.info("Waiting for Particl chain height %d", num_blocks) - for i in range(60): - particl_blocks = cls.swap_clients[0].callrpc("getblockcount") - print("particl_blocks", particl_blocks) - if particl_blocks >= num_blocks: - break - delay_event.wait(1) - assert particl_blocks >= num_blocks @classmethod - def tearDownClass(cls): - global stop_test - logging.info("Finalising") - stop_test = True - cls.update_thread.join() - cls.coins_update_thread.join() - for t in cls.http_threads: - t.stop() - t.join() - for c in cls.swap_clients: - c.finalise() + def addPIDInfo(cls, sc, i): + sc.setDaemonPID(Coins.DCR, cls.nmc_daemons[i].handle.pid) - stopDaemons(cls.daemons) - cls.http_threads.clear() - cls.swap_clients.clear() - cls.daemons.clear() + @classmethod + def addCoinSettings(cls, settings, datadir, node_id): + settings["chainclients"]["namecoin"] = { + "connection_type": "rpc", + "manage_daemon": False, + "rpcport": NMC_BASE_RPC_PORT + node_id, + "rpcuser": "test" + str(node_id), + "rpcpassword": "test_pass" + str(node_id), + "datadir": os.path.join(datadir, "nmc_" + str(node_id)), + "bindir": NAMECOIN_BINDIR, + "use_csv": True, + "use_segwit": True, + "blocks_confirmed": 1, + } - super(Test, cls).tearDownClass() - - def test_02_part_nmc(self): - logging.info("---------- Test PART to NMC") - swap_clients = self.swap_clients - - offer_id = swap_clients[0].postOffer( - Coins.PART, - Coins.NMC, - 100 * COIN, - 0.1 * COIN, - 100 * COIN, - SwapTypes.SELLER_FIRST - ) - - wait_for_offer(delay_event, swap_clients[1], offer_id) - offer = swap_clients[1].getOffer(offer_id) - bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[0], bid_id) - - swap_clients[0].acceptBid(bid_id) - - wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - - wait_for_bid( - delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60 - ) - wait_for_bid( - delay_event, - swap_clients[1], - bid_id, - BidStates.SWAP_COMPLETED, - sent=True, - wait_for=60, - ) - - js_0 = read_json_api(1800) - js_1 = read_json_api(1801) - assert js_0["num_swapping"] == 0 and js_0["num_watched_outputs"] == 0 - assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0 - - def test_03_nmc_part(self): - logging.info("---------- Test NMC to PART") - swap_clients = self.swap_clients - - offer_id = swap_clients[1].postOffer( - Coins.NMC, - Coins.PART, - 10 * COIN, - 9.0 * COIN, - 10 * COIN, - SwapTypes.SELLER_FIRST - ) - - wait_for_offer(delay_event, swap_clients[0], offer_id) - offer = swap_clients[0].getOffer(offer_id) - bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[1], bid_id) - swap_clients[1].acceptBid(bid_id) - - wait_for_in_progress(delay_event, swap_clients[0], bid_id, sent=True) - - wait_for_bid( - delay_event, - swap_clients[0], - bid_id, - BidStates.SWAP_COMPLETED, - sent=True, - wait_for=60, - ) - wait_for_bid( - delay_event, swap_clients[1], bid_id, BidStates.SWAP_COMPLETED, wait_for=60 - ) - - js_0 = read_json_api(1800) - js_1 = read_json_api(1801) - assert js_0["num_swapping"] == 0 and js_0["num_watched_outputs"] == 0 - assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0 - - def test_04_nmc_btc(self): - logging.info("---------- Test NMC to BTC") - swap_clients = self.swap_clients - - offer_id = swap_clients[0].postOffer( - Coins.NMC, - Coins.BTC, - 10 * COIN, - 0.1 * COIN, - 10 * COIN, - SwapTypes.SELLER_FIRST - ) - - wait_for_offer(delay_event, swap_clients[1], offer_id) - offer = swap_clients[1].getOffer(offer_id) - bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[0], bid_id) - swap_clients[0].acceptBid(bid_id) - - wait_for_in_progress(delay_event, swap_clients[1], bid_id, sent=True) - - wait_for_bid( - delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60 - ) - wait_for_bid( - delay_event, - swap_clients[1], - bid_id, - BidStates.SWAP_COMPLETED, - sent=True, - wait_for=60, - ) - - js_0 = read_json_api(1800) - js_1 = read_json_api(1801) - - assert js_0["num_swapping"] == 0 and js_0["num_watched_outputs"] == 0 - assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0 - - def test_05_refund(self): - # Seller submits initiate txn, buyer doesn't respond - logging.info("---------- Test refund, NMC to BTC") - swap_clients = self.swap_clients - - offer_id = swap_clients[0].postOffer( - Coins.NMC, - Coins.BTC, - 10 * COIN, - 0.1 * COIN, - 10 * COIN, - SwapTypes.SELLER_FIRST, - TxLockTypes.SEQUENCE_LOCK_BLOCKS, - 10, - ) - - wait_for_offer(delay_event, swap_clients[1], offer_id) - offer = swap_clients[1].getOffer(offer_id) - bid_id = swap_clients[1].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[0], bid_id) - swap_clients[1].abandonBid(bid_id) - swap_clients[0].acceptBid(bid_id) - - wait_for_bid( - delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60 - ) - wait_for_bid( - delay_event, - swap_clients[1], - bid_id, - BidStates.BID_ABANDONED, - sent=True, - wait_for=60, - ) - - js_0 = read_json_api(1800) - js_1 = read_json_api(1801) - assert js_0["num_swapping"] == 0 and js_0["num_watched_outputs"] == 0 - assert js_1["num_swapping"] == 0 and js_1["num_watched_outputs"] == 0 - - def test_06_self_bid(self): - logging.info("---------- Test same client, BTC to NMC") - swap_clients = self.swap_clients - - js_0_before = read_json_api(1800) - - offer_id = swap_clients[0].postOffer( - Coins.NMC, - Coins.BTC, - 10 * COIN, - 10 * COIN, - 10 * COIN, - SwapTypes.SELLER_FIRST - ) - - wait_for_offer(delay_event, swap_clients[0], offer_id) - offer = swap_clients[0].getOffer(offer_id) - bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[0], bid_id) - swap_clients[0].acceptBid(bid_id) - - wait_for_bid_tx_state( - delay_event, - swap_clients[0], - bid_id, - TxStates.TX_REDEEMED, - TxStates.TX_REDEEMED, - wait_for=60, - ) - wait_for_bid( - delay_event, swap_clients[0], bid_id, BidStates.SWAP_COMPLETED, wait_for=60 - ) - - js_0 = read_json_api(1800) - assert js_0["num_swapping"] == 0 and js_0["num_watched_outputs"] == 0 - assert ( - js_0["num_recv_bids"] == js_0_before["num_recv_bids"] + 1 - and js_0["num_sent_bids"] == js_0_before["num_sent_bids"] + 1 - ) - - def test_07_error(self): - logging.info("---------- Test error, BTC to NMC, set fee above bid value") - swap_clients = self.swap_clients - - offer_id = swap_clients[0].postOffer( - Coins.NMC, - Coins.BTC, - 0.01 * COIN, - 1.0 * COIN, - 0.01 * COIN, - SwapTypes.SELLER_FIRST - ) - - wait_for_offer(delay_event, swap_clients[0], offer_id) - offer = swap_clients[0].getOffer(offer_id) - bid_id = swap_clients[0].postBid(offer_id, offer.amount_from) - - wait_for_bid(delay_event, swap_clients[0], bid_id) - swap_clients[0].acceptBid(bid_id) - try: - swap_clients[0].getChainClientSettings(Coins.BTC)["override_feerate"] = 100.0 - swap_clients[0].getChainClientSettings(Coins.NMC)["override_feerate"] = 100.0 - wait_for_bid( - delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60 + @classmethod + def prepareExtraCoins(cls): + ci0 = cls.swap_clients[0].ci(cls.test_coin) + if not cls.restore_instance: + cls.nmc_addr = ci0.rpc_wallet("getnewaddress", ["mining_addr", "bech32"]) + else: + addrs = ci0.rpc_wallet( + "getaddressesbylabel", + [ + "mining_addr", + ], ) - swap_clients[0].abandonBid(bid_id) - finally: - del swap_clients[0].getChainClientSettings(Coins.BTC)["override_feerate"] - del swap_clients[0].getChainClientSettings(Coins.NMC)["override_feerate"] + cls.nmc_addr = addrs.keys()[0] - def pass_99_delay(self): - global stop_test - logging.info("Delay") - for i in range(60 * 5): - if stop_test: - break - time.sleep(1) - print("delay", i) - stop_test = True + num_blocks: int = 500 + if ci0.rpc("getblockcount") < num_blocks: + logging.info(f"Mining {num_blocks} Namecoin blocks to {cls.nmc_addr}") + ci0.rpc("generatetoaddress", [num_blocks, cls.nmc_addr]) + logging.info("NMC blocks: {}".format(ci0.rpc("getblockcount"))) + + def test_007_hdwallet(self): + logging.info("---------- Test {} hdwallet".format(self.test_coin_from.name)) + + test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b" + test_wif = ( + self.swap_clients[0] + .ci(self.test_coin_from) + .encodeKey(bytes.fromhex(test_seed)) + ) + new_wallet_name = random.randbytes(10).hex() + self.callnoderpc( + "createwallet", + [new_wallet_name, False, False, "", False, USE_DESCRIPTOR_WALLETS], + ) + self.callnoderpc("sethdseed", [True, test_wif], wallet=new_wallet_name) + addr = self.callnoderpc( + "getnewaddress", ["add test", "bech32"], wallet=new_wallet_name + ) + self.callnoderpc("unloadwallet", [new_wallet_name]) + assert addr == "ncrt1qps7hnjd866e9ynxadgseprkc2l56m00dxkl7pk" + + def test_012_p2sh_p2wsh(self): + # Fee rate + pass + + def test_02_sh_part_coin(self): + self.prepare_balance(self.test_coin, 200.0, 1801, 1800) + run_test_success_path(self, Coins.PART, self.test_coin) + + def test_03_sh_coin_part(self): + run_test_success_path(self, self.test_coin, Coins.PART) + + def test_04_sh_part_coin_bad_ptx(self): + self.prepare_balance(self.test_coin, 200.0, 1801, 1800) + run_test_bad_ptx(self, Coins.PART, self.test_coin) + + def test_05_sh_coin_part_bad_ptx(self): + self.prepare_balance(self.test_coin, 200.0, 1801, 1800) + run_test_bad_ptx(self, self.test_coin, Coins.PART) + + def test_06_sh_part_coin_itx_refund(self): + run_test_itx_refund(self, Coins.PART, self.test_coin) + + def test_07_sh_coin_part_itx_refund(self): + self.prepare_balance(self.test_coin, 200.0, 1801, 1800) + run_test_itx_refund(self, self.test_coin, Coins.PART) + + def test_01_b_full_swap_reverse(self): + self.prepare_balance(self.test_coin, 100.0, 1801, 1800) + self.do_test_01_full_swap(Coins.XMR, self.test_coin_from) if __name__ == "__main__": diff --git a/tests/basicswap/test_btc_xmr.py b/tests/basicswap/test_btc_xmr.py index 866fbed..32d2082 100644 --- a/tests/basicswap/test_btc_xmr.py +++ b/tests/basicswap/test_btc_xmr.py @@ -59,7 +59,6 @@ from basicswap.contrib.test_framework.script import ( from .test_xmr import BaseTest, test_delay_event, callnoderpc logger = logging.getLogger() - test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b" @@ -666,7 +665,7 @@ class TestFunctions(BaseTest): balance_from_before: float = self.getBalance(jsw, coin_from) self.prepare_balance( coin_to, - balance_from_before + 1, + balance_from_before * 3, 1800 + id_bidder, 1801 if coin_to in (Coins.XMR,) else 1800, ) @@ -718,6 +717,7 @@ class TestFunctions(BaseTest): assert False, "Should fail" amt_swap -= ci_from.make_int(1) + rate_swap = ci_to.make_int(1.0, r=1) offer_id = swap_clients[id_offerer].postOffer( coin_from, coin_to, @@ -770,6 +770,8 @@ class TestFunctions(BaseTest): class BasicSwapTest(TestFunctions): + test_fee_rate: int = 1000 # sats/kvB + @classmethod def setUpClass(cls): super(BasicSwapTest, cls).setUpClass() @@ -1236,12 +1238,7 @@ class BasicSwapTest(TestFunctions): swap_client = self.swap_clients[0] ci = swap_client.ci(self.test_coin_from) - addr_1 = ci.rpc_wallet( - "getnewaddress", - [ - "gettxout test 1", - ], - ) + addr_1 = ci.getNewAddress(True, "gettxout test 1") txid = ci.rpc_wallet("sendtoaddress", [addr_1, 1.0]) assert len(txid) == 64 @@ -1266,12 +1263,7 @@ class BasicSwapTest(TestFunctions): else: assert addr_1 in txout["scriptPubKey"]["addresses"] # Spend - addr_2 = ci.rpc_wallet( - "getnewaddress", - [ - "gettxout test 2", - ], - ) + addr_2 = ci.getNewAddress(True, "gettxout test 2") tx_funded = ci.rpc( "createrawtransaction", [[{"txid": utxo["txid"], "vout": utxo["vout"]}], {addr_2: 0.99}], @@ -1297,12 +1289,7 @@ class BasicSwapTest(TestFunctions): logging.info("---------- Test {} scantxoutset".format(self.test_coin_from.name)) ci = self.swap_clients[0].ci(self.test_coin_from) - addr_1 = ci.rpc_wallet( - "getnewaddress", - [ - "scantxoutset test", - ], - ) + addr_1 = ci.getNewAddress(True, "scantxoutset test") txid = ci.rpc_wallet("sendtoaddress", [addr_1, 1.0]) assert len(txid) == 64 @@ -1324,9 +1311,6 @@ class BasicSwapTest(TestFunctions): # Record unspents before createSCLockTx as the used ones will be locked unspents = ci.rpc_wallet("listunspent") - # fee_rate is in sats/kvB - fee_rate: int = 1000 - a = ci.getNewRandomKey() b = ci.getNewRandomKey() @@ -1335,7 +1319,7 @@ class BasicSwapTest(TestFunctions): lock_tx_script = pi.genScriptLockTxScript(ci, A, B) lock_tx = ci.createSCLockTx(amount, lock_tx_script) - lock_tx = ci.fundSCLockTx(lock_tx, fee_rate) + lock_tx = ci.fundSCLockTx(lock_tx, self.test_fee_rate) lock_tx = ci.signTxWithWallet(lock_tx) unspents_after = ci.rpc_wallet("listunspent") @@ -1345,7 +1329,7 @@ class BasicSwapTest(TestFunctions): txid = tx_decoded["txid"] vsize = tx_decoded["vsize"] - expect_fee_int = round(fee_rate * vsize / 1000) + expect_fee_int = round(self.test_fee_rate * vsize / 1000) out_value: int = 0 for txo in tx_decoded["vout"]: @@ -1372,7 +1356,7 @@ class BasicSwapTest(TestFunctions): pkh_out = ci.decodeAddress(addr_out) fee_info = {} lock_spend_tx = ci.createSCLockSpendTx( - lock_tx, lock_tx_script, pkh_out, fee_rate, fee_info=fee_info + lock_tx, lock_tx_script, pkh_out, self.test_fee_rate, fee_info=fee_info ) vsize_estimated: int = fee_info["vsize"] @@ -1400,11 +1384,11 @@ class BasicSwapTest(TestFunctions): v = ci.getNewRandomKey() s = ci.getNewRandomKey() S = ci.getPubkey(s) - lock_tx_b_txid = ci.publishBLockTx(v, S, amount, fee_rate) + lock_tx_b_txid = ci.publishBLockTx(v, S, amount, self.test_fee_rate) addr_out = ci.getNewAddress(True) lock_tx_b_spend_txid = ci.spendBLockTx( - lock_tx_b_txid, addr_out, v, s, amount, fee_rate, 0 + lock_tx_b_txid, addr_out, v, s, amount, self.test_fee_rate, 0 ) lock_tx_b_spend = ci.getTransaction(lock_tx_b_spend_txid) if lock_tx_b_spend is None: @@ -1635,7 +1619,9 @@ class BasicSwapTest(TestFunctions): wallet=new_wallet_name, ) - addr = self.callnoderpc("getnewaddress", wallet=new_wallet_name) + addr = self.callnoderpc( + "getnewaddress", ["test descriptors"], wallet=new_wallet_name + ) addr_info = self.callnoderpc( "getaddressinfo", [ @@ -1645,7 +1631,8 @@ class BasicSwapTest(TestFunctions): ) assert addr_info["hdmasterfingerprint"] == "a55b7ea9" assert addr_info["hdkeypath"] == "m/0h/0h/0h" - assert addr == "bcrt1qps7hnjd866e9ynxadgseprkc2l56m00dvwargr" + if self.test_coin_from == Coins.BTC: + assert addr == "bcrt1qps7hnjd866e9ynxadgseprkc2l56m00dvwargr" addr_change = self.callnoderpc("getrawchangeaddress", wallet=new_wallet_name) addr_info = self.callnoderpc( @@ -1657,7 +1644,8 @@ class BasicSwapTest(TestFunctions): ) assert addr_info["hdmasterfingerprint"] == "a55b7ea9" assert addr_info["hdkeypath"] == "m/0h/1h/0h" - assert addr_change == "bcrt1qdl9ryxkqjltv42lhfnqgdjf9tagxsjpp2xak9a" + if self.test_coin_from == Coins.BTC: + assert addr_change == "bcrt1qdl9ryxkqjltv42lhfnqgdjf9tagxsjpp2xak9a" desc_watch = descsum_create(f"addr({addr})") self.callnoderpc( @@ -1683,7 +1671,13 @@ class BasicSwapTest(TestFunctions): # Test that addresses can be generated beyond range in listdescriptors for i in range(2000): - self.callnoderpc("getnewaddress", wallet=new_wallet_name) + self.callnoderpc( + "getnewaddress", + [ + f"t{i}", + ], + wallet=new_wallet_name, + ) self.callnoderpc("unloadwallet", [new_wallet_name]) self.callnoderpc("unloadwallet", [new_watch_wallet_name]) From e9ed334a541ed025b698b1955e6997cb56d985ef Mon Sep 17 00:00:00 2001 From: tecnovert Date: Sun, 30 Mar 2025 00:32:08 +0200 Subject: [PATCH 5/8] nmc: Use descriptor wallets by default. --- basicswap/bin/prepare.py | 17 ++++++-- basicswap/chainparams.py | 6 +++ basicswap/interface/btc.py | 62 +++++++++++++++++++++++--- basicswap/interface/nmc.py | 54 ----------------------- tests/basicswap/extended/test_nmc.py | 19 ++++++-- tests/basicswap/test_btc_xmr.py | 65 ++++++++++++++++++++++++++-- 6 files changed, 152 insertions(+), 71 deletions(-) diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index 233e30f..f70f602 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -379,6 +379,12 @@ def getWalletName(coin_params: str, default_name: str, prefix_override=None) -> return wallet_name +def getDescriptorWalletOption(coin_params): + ticker: str = coin_params["ticker"] + default_option: bool = True if ticker in ("NMC",) else False + return toBool(os.getenv(ticker + "_USE_DESCRIPTORS", default_option)) + + def getKnownVersion(coin_name: str) -> str: version, version_tag, _ = known_coins[coin_name] return version + version_tag @@ -1928,11 +1934,15 @@ def initialise_wallets( ], ) if use_descriptors: + watch_wallet_name = coin_settings["watch_wallet_name"] + logger.info( + f'Creating wallet "{watch_wallet_name}" for {getCoinName(c)}.' + ) swap_client.callcoinrpc( c, "createwallet", [ - coin_settings["watch_wallet_name"], + watch_wallet_name, True, True, "", @@ -2596,9 +2606,8 @@ def main(): coin_settings["wallet_name"] = set_name ticker: str = coin_params["ticker"] - if toBool(os.getenv(ticker + "_USE_DESCRIPTORS", False)): - - if coin_id not in (Coins.BTC,): + if getDescriptorWalletOption(coin_params): + if coin_id not in (Coins.BTC, Coins.NMC): raise ValueError(f"Descriptor wallet unavailable for {coin_name}") coin_settings["use_descriptors"] = True diff --git a/basicswap/chainparams.py b/basicswap/chainparams.py index f4e36d5..efbb58b 100644 --- a/basicswap/chainparams.py +++ b/basicswap/chainparams.py @@ -58,6 +58,8 @@ chainparams = { "bip44": 44, "min_amount": 100000, "max_amount": 10000000 * COIN, + "ext_public_key_prefix": 0x696E82D1, + "ext_secret_key_prefix": 0x8F1DAEB8, }, "testnet": { "rpcport": 51935, @@ -69,6 +71,8 @@ chainparams = { "bip44": 1, "min_amount": 100000, "max_amount": 10000000 * COIN, + "ext_public_key_prefix": 0xE1427800, + "ext_secret_key_prefix": 0x04889478, }, "regtest": { "rpcport": 51936, @@ -80,6 +84,8 @@ chainparams = { "bip44": 1, "min_amount": 100000, "max_amount": 10000000 * COIN, + "ext_public_key_prefix": 0xE1427800, + "ext_secret_key_prefix": 0x04889478, }, }, Coins.BTC: { diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index 618ddec..b8656c8 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -1862,20 +1862,70 @@ class BTCInterface(Secp256k1Interface): "Could not find address with enough funds for proof", ) - self._log.debug("sign_for_addr %s", sign_for_addr) + self._log.debug(f"sign_for_addr {sign_for_addr}") + funds_addr: str = sign_for_addr if ( self.using_segwit() ): # TODO: Use isSegwitAddress when scantxoutset can use combo # 'Address does not refer to key' for non p2pkh pkh = self.decodeAddress(sign_for_addr) sign_for_addr = self.pkh_to_address(pkh) - self._log.debug("sign_for_addr converted %s", sign_for_addr) + self._log.debug(f"sign_for_addr converted {sign_for_addr}") - signature = self.rpc_wallet( - "signmessage", - [sign_for_addr, sign_for_addr + "_swap_proof_" + extra_commit_bytes.hex()], - ) + if self._use_descriptors: + # https://github.com/bitcoin/bitcoin/issues/10542 + # https://github.com/bitcoin/bitcoin/issues/26046 + priv_keys = self.rpc_wallet( + "listdescriptors", + [ + True, + ], + ) + addr_info = self.rpc_wallet( + "getaddressinfo", + [ + funds_addr, + ], + ) + hdkeypath = addr_info["hdkeypath"] + + sign_for_address_key = None + for descriptor in priv_keys["descriptors"]: + if descriptor["active"] is False or descriptor["internal"] is True: + continue + desc = descriptor["desc"] + assert desc.startswith("wpkh(") + ext_key = desc[5:].split(")")[0].split("/", 1)[0] + ext_key_data = decodeAddress(ext_key)[4:] + ci_part = self._sc.ci(Coins.PART) + ext_key_data_part = ci_part.encode_secret_extkey(ext_key_data) + rv = ci_part.rpc_wallet( + "extkey", ["info", ext_key_data_part, hdkeypath] + ) + extkey_derived = rv["key_info"]["result"] + ext_key_data = decodeAddress(extkey_derived)[4:] + ek = ExtKeyPair() + ek.decode(ext_key_data) + sign_for_address_key = self.encodeKey(ek._key) + break + assert sign_for_address_key is not None + signature = self.rpc( + "signmessagewithprivkey", + [ + sign_for_address_key, + sign_for_addr + "_swap_proof_" + extra_commit_bytes.hex(), + ], + ) + del priv_keys + else: + signature = self.rpc_wallet( + "signmessage", + [ + sign_for_addr, + sign_for_addr + "_swap_proof_" + extra_commit_bytes.hex(), + ], + ) prove_utxos = [] # TODO: Send specific utxos return (sign_for_addr, signature, prove_utxos) diff --git a/basicswap/interface/nmc.py b/basicswap/interface/nmc.py index 19aa48e..0214857 100644 --- a/basicswap/interface/nmc.py +++ b/basicswap/interface/nmc.py @@ -14,57 +14,3 @@ class NMCInterface(BTCInterface): @staticmethod def coin_type(): return Coins.NMC - - def lockNonSegwitPrevouts(self) -> None: - # For tests - # NMC Seems to ignore utxo locks - unspent = self.rpc_wallet("listunspent") - - to_lock = [] - for u in unspent: - if u.get("spendable", False) is False: - continue - if "desc" in u: - desc = u["desc"] - if self.use_p2shp2wsh(): - if not desc.startswith("sh(wpkh"): - to_lock.append( - { - "txid": u["txid"], - "vout": u["vout"], - "amount": u["amount"], - } - ) - else: - if not desc.startswith("wpkh"): - to_lock.append( - { - "txid": u["txid"], - "vout": u["vout"], - "amount": u["amount"], - } - ) - - if len(to_lock) > 0: - self._log.debug(f"Spending {len(to_lock)} non segwit prevouts") - addr_out = self.rpc_wallet( - "getnewaddress", ["convert non segwit", "bech32"] - ) - prevouts = [] - sum_amount: int = 0 - for utxo in to_lock: - prevouts.append( - { - "txid": utxo["txid"], - "vout": utxo["vout"], - } - ) - sum_amount += self.make_int(utxo["amount"]) - - fee = 100000 * len(prevouts) - funded_tx = self.rpc( - "createrawtransaction", - [prevouts, {addr_out: self.format_amount(sum_amount - fee)}], - ) - signed_tx = self.rpc_wallet("signrawtransactionwithwallet", [funded_tx]) - self.rpc("sendrawtransaction", [signed_tx["hex"]]) diff --git a/tests/basicswap/extended/test_nmc.py b/tests/basicswap/extended/test_nmc.py index ac839e9..aa612ef 100644 --- a/tests/basicswap/extended/test_nmc.py +++ b/tests/basicswap/extended/test_nmc.py @@ -54,7 +54,7 @@ NAMECOIND = os.getenv("NAMECOIND", "namecoind" + cfg.bin_suffix) NAMECOIN_CLI = os.getenv("NAMECOIN_CLI", "namecoin-cli" + cfg.bin_suffix) NAMECOIN_TX = os.getenv("NAMECOIN_TX", "namecoin-tx" + cfg.bin_suffix) -USE_DESCRIPTOR_WALLETS = toBool(os.getenv("USE_DESCRIPTOR_WALLETS", False)) +NMC_USE_DESCRIPTORS = toBool(os.getenv("NMC_USE_DESCRIPTORS", True)) NMC_BASE_PORT = 8136 NMC_BASE_RPC_PORT = 8146 @@ -112,7 +112,7 @@ class TestNMC(BasicSwapTest): base_rpc_port = NMC_BASE_RPC_PORT nmc_addr = None max_fee: int = 200000 - test_fee_rate: int = 10000 # sats/kvB + test_fee_rate: int = 100000 # sats/kvB def mineBlock(self, num_blocks: int = 1) -> None: self.callnoderpc("generatetoaddress", [num_blocks, self.nmc_addr]) @@ -160,8 +160,13 @@ class TestNMC(BasicSwapTest): if len(nmc_rpc("listwallets")) < 1: nmc_rpc( "createwallet", - ["wallet.dat", False, False, "", False, USE_DESCRIPTOR_WALLETS], + ["wallet.dat", False, True, "", False, NMC_USE_DESCRIPTORS], ) + if NMC_USE_DESCRIPTORS: + nmc_rpc( + "createwallet", + ["bsx_watch", True, True, "", False, True], + ) @classmethod def addPIDInfo(cls, sc, i): @@ -180,12 +185,18 @@ class TestNMC(BasicSwapTest): "use_csv": True, "use_segwit": True, "blocks_confirmed": 1, + "use_descriptors": NMC_USE_DESCRIPTORS, } + if NMC_USE_DESCRIPTORS: + settings["chainclients"]["namecoin"]["watch_wallet_name"] = "bsx_watch" @classmethod def prepareExtraCoins(cls): ci0 = cls.swap_clients[0].ci(cls.test_coin) if not cls.restore_instance: + for sc in cls.swap_clients: + ci = sc.ci(cls.test_coin) + ci.initialiseWallet(ci.getNewRandomKey()) cls.nmc_addr = ci0.rpc_wallet("getnewaddress", ["mining_addr", "bech32"]) else: addrs = ci0.rpc_wallet( @@ -214,7 +225,7 @@ class TestNMC(BasicSwapTest): new_wallet_name = random.randbytes(10).hex() self.callnoderpc( "createwallet", - [new_wallet_name, False, False, "", False, USE_DESCRIPTOR_WALLETS], + [new_wallet_name, False, False, "", False, NMC_USE_DESCRIPTORS], ) self.callnoderpc("sethdseed", [True, test_wif], wallet=new_wallet_name) addr = self.callnoderpc( diff --git a/tests/basicswap/test_btc_xmr.py b/tests/basicswap/test_btc_xmr.py index 32d2082..a79c5a8 100644 --- a/tests/basicswap/test_btc_xmr.py +++ b/tests/basicswap/test_btc_xmr.py @@ -26,6 +26,9 @@ from basicswap.db import ( from basicswap.util import ( make_int, ) +from basicswap.util.address import ( + decodeAddress, +) from basicswap.util.extkey import ExtKeyPair from basicswap.interface.base import Curves from tests.basicswap.util import ( @@ -182,7 +185,7 @@ class TestFunctions(BaseTest): bid0 = read_json_api(1800 + id_offerer, f"bids/{bid_id.hex()}") bid1 = read_json_api(1800 + id_bidder, f"bids/{bid_id.hex()}") - tolerance = 1 + tolerance = 2 assert bid0["ticker_from"] == ci_from.ticker() assert bid1["ticker_from"] == ci_from.ticker() assert bid0["ticker_to"] == ci_to.ticker() @@ -1180,6 +1183,10 @@ class BasicSwapTest(TestFunctions): logging.info("---------- Test {} hdwallet".format(self.test_coin_from.name)) ci = self.swap_clients[0].ci(self.test_coin_from) + if hasattr(ci, "_use_descriptors") and ci._use_descriptors: + logging.warning("Skipping test") + return + test_wif = ( self.swap_clients[0] .ci(self.test_coin_from) @@ -1310,7 +1317,7 @@ class BasicSwapTest(TestFunctions): # Record unspents before createSCLockTx as the used ones will be locked unspents = ci.rpc_wallet("listunspent") - + lockedunspents_before = ci.rpc_wallet("listlockunspent") a = ci.getNewRandomKey() b = ci.getNewRandomKey() @@ -1322,8 +1329,17 @@ class BasicSwapTest(TestFunctions): lock_tx = ci.fundSCLockTx(lock_tx, self.test_fee_rate) lock_tx = ci.signTxWithWallet(lock_tx) + # Check that inputs were locked + lockedunspents = ci.rpc_wallet("listlockunspent") + assert len(lockedunspents) > len(lockedunspents_before) unspents_after = ci.rpc_wallet("listunspent") - assert len(unspents) > len(unspents_after) + for utxo in unspents_after: + for locked_utxo in lockedunspents: + if ( + locked_utxo["txid"] == utxo["txid"] + and locked_utxo["vout"] == utxo["vout"] + ): + raise ValueError("Locked utxo in listunspent") tx_decoded = ci.rpc("decoderawtransaction", [lock_tx.hex()]) txid = tx_decoded["txid"] @@ -1679,6 +1695,49 @@ class BasicSwapTest(TestFunctions): wallet=new_wallet_name, ) + # https://github.com/bitcoin/bitcoin/issues/10542 + # https://github.com/bitcoin/bitcoin/issues/26046 + sign_for_address: str = self.callnoderpc( + "getnewaddress", + [ + "sign address", + ], + wallet=new_wallet_name, + ) + priv_keys = self.callnoderpc("listdescriptors", [True], wallet=new_wallet_name) + addr_info = self.callnoderpc( + "getaddressinfo", [sign_for_address], wallet=new_wallet_name + ) + hdkeypath = addr_info["hdkeypath"] + + sign_for_address_key = None + for descriptor in priv_keys["descriptors"]: + if descriptor["active"] is False or descriptor["internal"] is True: + continue + desc = descriptor["desc"] + assert desc.startswith("wpkh(") + ext_key = desc[5:].split(")")[0].split("/", 1)[0] + ext_key_data = decodeAddress(ext_key)[4:] + ci_part = self.swap_clients[0].ci(Coins.PART) + ext_key_data_part = ci_part.encode_secret_extkey(ext_key_data) + rv = ci_part.rpc_wallet("extkey", ["info", ext_key_data_part, hdkeypath]) + extkey_derived = rv["key_info"]["result"] + ext_key_data = decodeAddress(extkey_derived)[4:] + ek = ExtKeyPair() + ek.decode(ext_key_data) + addr = ci.encodeSegwitAddress(ci.getAddressHashFromKey(ek._key)) + assert addr == sign_for_address + sign_for_address_key = ci.encodeKey(ek._key) + break + assert sign_for_address_key is not None + sign_message: str = "Would be better if dumpprivkey or signmessage worked" + sig = self.callnoderpc( + "signmessagewithprivkey", + [sign_for_address_key, sign_message], + wallet=new_wallet_name, + ) + assert ci.verifyMessage(sign_for_address, sign_message, sig) + self.callnoderpc("unloadwallet", [new_wallet_name]) self.callnoderpc("unloadwallet", [new_watch_wallet_name]) From 6b724ece84c4beee23c2743e871c4f7d317ce2b5 Mon Sep 17 00:00:00 2001 From: tecnovert Date: Sun, 30 Mar 2025 22:36:03 +0200 Subject: [PATCH 6/8] nmc: Add to test_xmr_persistent. --- basicswap/bin/prepare.py | 111 ++++++++++-------- basicswap/bin/run.py | 6 +- basicswap/static/images/coins/Namecoin-20.png | Bin 0 -> 743 bytes basicswap/static/images/coins/Namecoin.png | Bin 0 -> 4282 bytes basicswap/static/js/bids_available.js | 20 ++-- basicswap/static/js/modules/api-manager.js | 4 +- basicswap/static/js/modules/cache-manager.js | 3 +- basicswap/static/js/modules/config-manager.js | 7 +- basicswap/static/js/modules/wallet-manager.js | 3 + basicswap/static/js/offers.js | 49 ++++---- basicswap/static/js/pricechart.js | 2 +- basicswap/static/js/swaps_in_progress.js | 11 +- basicswap/templates/offers.html | 9 +- tests/basicswap/common_xmr.py | 108 +++++++++++++---- tests/basicswap/extended/test_nmc.py | 1 + .../basicswap/extended/test_xmr_persistent.py | 34 +++++- 16 files changed, 240 insertions(+), 128 deletions(-) create mode 100644 basicswap/static/images/coins/Namecoin-20.png create mode 100644 basicswap/static/images/coins/Namecoin.png diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index f70f602..f513c21 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -117,7 +117,9 @@ known_coins = { "dogecoin": (DOGECOIN_VERSION, DOGECOIN_VERSION_TAG, ("tecnovert",)), } -disabled_coins = ["navcoin"] +disabled_coins = [ + "navcoin", +] expected_key_ids = { "tecnovert": ("13F13651C9CF0D6B",), @@ -207,6 +209,7 @@ DCR_RPC_PWD = os.getenv("DCR_RPC_PWD", random.randbytes(random.randint(14, 18)). NMC_RPC_HOST = os.getenv("NMC_RPC_HOST", "127.0.0.1") NMC_RPC_PORT = int(os.getenv("NMC_RPC_PORT", 19698)) +NMC_PORT = int(os.getenv("NMC_PORT", 8134)) NMC_ONION_PORT = int(os.getenv("NMC_ONION_PORT", 9698)) NMC_RPC_USER = os.getenv("NMC_RPC_USER", "") NMC_RPC_PWD = os.getenv("NMC_RPC_PWD", "") @@ -1349,6 +1352,8 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): fp.write("printtoconsole=0\n") fp.write("daemon=0\n") fp.write(f"wallet={wallet_name}\n") + if "watch_wallet_name" in core_settings: + fp.write("wallet={}\n".format(core_settings["watch_wallet_name"])) if tor_control_password is not None: writeTorSettings(fp, coin, core_settings, tor_control_password) @@ -1408,6 +1413,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): fp.write("deprecatedrpc=create_bdb\n") fp.write("addresstype=bech32\n") fp.write("changetype=bech32\n") + fp.write("fallbackfee=0.001\n") # minrelaytxfee elif coin == "pivx": params_dir = os.path.join(data_dir, "pivx-params") downloadPIVXParams(params_dir) @@ -2391,22 +2397,6 @@ def main(): "core_version_no": getKnownVersion("bitcoin"), "core_version_group": 28, }, - "bitcoincash": { - "connection_type": "rpc", - "manage_daemon": shouldManageDaemon("BCH"), - "rpchost": BCH_RPC_HOST, - "rpcport": BCH_RPC_PORT + port_offset, - "onionport": BCH_ONION_PORT + port_offset, - "datadir": os.getenv("BCH_DATA_DIR", os.path.join(data_dir, "bitcoincash")), - "bindir": os.path.join(bin_dir, "bitcoincash"), - "port": BCH_PORT + port_offset, - "config_filename": "bitcoin.conf", - "use_segwit": False, - "blocks_confirmed": 1, - "conf_target": 2, - "core_version_no": getKnownVersion("bitcoincash"), - "core_version_group": 22, - }, "litecoin": { "connection_type": "rpc", "manage_daemon": shouldManageDaemon("LTC"), @@ -2422,22 +2412,6 @@ def main(): "core_version_group": 20, "min_relay_fee": 0.00001, }, - "dogecoin": { - "connection_type": "rpc", - "manage_daemon": shouldManageDaemon("DOGE"), - "rpchost": DOGE_RPC_HOST, - "rpcport": DOGE_RPC_PORT + port_offset, - "onionport": DOGE_ONION_PORT + port_offset, - "datadir": os.getenv("DOGE_DATA_DIR", os.path.join(data_dir, "dogecoin")), - "bindir": os.path.join(bin_dir, "dogecoin"), - "use_segwit": False, - "use_csv": False, - "blocks_confirmed": 2, - "conf_target": 2, - "core_version_no": getKnownVersion("dogecoin"), - "core_version_group": 23, - "min_relay_fee": 0.01, # RECOMMENDED_MIN_TX_FEE - }, "decred": { "connection_type": "rpc", "manage_daemon": shouldManageDaemon("DCR"), @@ -2468,6 +2442,7 @@ def main(): "onionport": NMC_ONION_PORT + port_offset, "datadir": os.getenv("NMC_DATA_DIR", os.path.join(data_dir, "namecoin")), "bindir": os.path.join(bin_dir, "namecoin"), + "port": NMC_PORT + port_offset, "use_segwit": True, "use_csv": True, "blocks_confirmed": 1, @@ -2499,6 +2474,28 @@ def main(): "core_version_no": getKnownVersion("monero"), "core_type_group": "xmr", }, + "wownero": { + "connection_type": "rpc", + "manage_daemon": shouldManageDaemon("WOW"), + "manage_wallet_daemon": shouldManageDaemon("WOW_WALLET"), + "rpcport": WOW_RPC_PORT + port_offset, + "zmqport": WOW_ZMQ_PORT + port_offset, + "walletrpcport": WOW_WALLET_RPC_PORT + port_offset, + "rpchost": WOW_RPC_HOST, + "trusted_daemon": extra_opts.get("trust_remote_node", "auto"), + "walletrpchost": WOW_WALLET_RPC_HOST, + "walletrpcuser": WOW_WALLET_RPC_USER, + "walletrpcpassword": WOW_WALLET_RPC_PWD, + "datadir": os.getenv("WOW_DATA_DIR", os.path.join(data_dir, "wownero")), + "bindir": os.path.join(bin_dir, "wownero"), + "restore_height": wow_restore_height, + "blocks_confirmed": 2, + "rpctimeout": 60, + "walletrpctimeout": 120, + "walletrpctimeoutlong": 300, + "core_version_no": getKnownVersion("wownero"), + "core_type_group": "xmr", + }, "pivx": { "connection_type": "rpc", "manage_daemon": shouldManageDaemon("PIVX"), @@ -2562,27 +2559,37 @@ def main(): "chain_lookups": "local", "startup_tries": 40, }, - "wownero": { + "bitcoincash": { "connection_type": "rpc", - "manage_daemon": shouldManageDaemon("WOW"), - "manage_wallet_daemon": shouldManageDaemon("WOW_WALLET"), - "rpcport": WOW_RPC_PORT + port_offset, - "zmqport": WOW_ZMQ_PORT + port_offset, - "walletrpcport": WOW_WALLET_RPC_PORT + port_offset, - "rpchost": WOW_RPC_HOST, - "trusted_daemon": extra_opts.get("trust_remote_node", "auto"), - "walletrpchost": WOW_WALLET_RPC_HOST, - "walletrpcuser": WOW_WALLET_RPC_USER, - "walletrpcpassword": WOW_WALLET_RPC_PWD, - "datadir": os.getenv("WOW_DATA_DIR", os.path.join(data_dir, "wownero")), - "bindir": os.path.join(bin_dir, "wownero"), - "restore_height": wow_restore_height, + "manage_daemon": shouldManageDaemon("BCH"), + "rpchost": BCH_RPC_HOST, + "rpcport": BCH_RPC_PORT + port_offset, + "onionport": BCH_ONION_PORT + port_offset, + "datadir": os.getenv("BCH_DATA_DIR", os.path.join(data_dir, "bitcoincash")), + "bindir": os.path.join(bin_dir, "bitcoincash"), + "port": BCH_PORT + port_offset, + "config_filename": "bitcoin.conf", + "use_segwit": False, + "blocks_confirmed": 1, + "conf_target": 2, + "core_version_no": getKnownVersion("bitcoincash"), + "core_version_group": 22, + }, + "dogecoin": { + "connection_type": "rpc", + "manage_daemon": shouldManageDaemon("DOGE"), + "rpchost": DOGE_RPC_HOST, + "rpcport": DOGE_RPC_PORT + port_offset, + "onionport": DOGE_ONION_PORT + port_offset, + "datadir": os.getenv("DOGE_DATA_DIR", os.path.join(data_dir, "dogecoin")), + "bindir": os.path.join(bin_dir, "dogecoin"), + "use_segwit": False, + "use_csv": False, "blocks_confirmed": 2, - "rpctimeout": 60, - "walletrpctimeout": 120, - "walletrpctimeoutlong": 300, - "core_version_no": getKnownVersion("wownero"), - "core_type_group": "xmr", + "conf_target": 2, + "core_version_no": getKnownVersion("dogecoin"), + "core_version_group": 23, + "min_relay_fee": 0.01, # RECOMMENDED_MIN_TX_FEE }, } diff --git a/basicswap/bin/run.py b/basicswap/bin/run.py index 091e857..c34c721 100755 --- a/basicswap/bin/run.py +++ b/basicswap/bin/run.py @@ -265,7 +265,11 @@ def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=Fal # As BCH may use port 8334, disable it here. # When tor is enabled a bind option for the onionport will be added to bitcoin.conf. # https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-28.0.md?plain=1#L84 - if prepare is False and use_tor_proxy is False and coin_id == Coins.BTC: + if ( + prepare is False + and use_tor_proxy is False + and coin_id in (Coins.BTC, Coins.NMC) + ): port: int = coin_settings.get("port", 8333) extra_args.append(f"--bind=0.0.0.0:{port}") return extra_args diff --git a/basicswap/static/images/coins/Namecoin-20.png b/basicswap/static/images/coins/Namecoin-20.png new file mode 100644 index 0000000000000000000000000000000000000000..204808d0076f9c5a086911ecaac5c1e1e8d17256 GIT binary patch literal 743 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 literal 0 HcmV?d00001 diff --git a/basicswap/static/js/bids_available.js b/basicswap/static/js/bids_available.js index 3cb11e8..53d5251 100644 --- a/basicswap/static/js/bids_available.js +++ b/basicswap/static/js/bids_available.js @@ -10,6 +10,7 @@ const COIN_NAME_TO_SYMBOL = { 'Firo': 'FIRO', 'Dash': 'DASH', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Wownero': 'WOW', 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' @@ -335,13 +336,13 @@ const createBidTableRow = async (bid) => {
- Offer ID: + Offer ID: ${formatAddress(bid.offer_id)}
- Bid ID: + Bid ID: ${formatAddress(bid.bid_id)} @@ -366,8 +367,8 @@ const createBidTableRow = async (bid) => {
- ${bid.coin_from} @@ -375,15 +376,14 @@ const createBidTableRow = async (bid) => { - ${bid.coin_to}
-
@@ -410,7 +410,7 @@ const createBidTableRow = async (bid) => { - Accept @@ -506,13 +506,13 @@ const createDetailsColumn = (bid, identity, uniqueId) => `
- Offer ID: + Offer ID: ${formatAddress(bid.offer_id)}
- Bid ID: + Bid ID: ${formatAddress(bid.bid_id)} diff --git a/basicswap/static/js/modules/api-manager.js b/basicswap/static/js/modules/api-manager.js index 9f299e1..e166593 100644 --- a/basicswap/static/js/modules/api-manager.js +++ b/basicswap/static/js/modules/api-manager.js @@ -220,7 +220,7 @@ const ApiManager = (function() { .filter(coin => coin.usesCoinGecko) .map(coin => coin.name) .join(',') : - 'bitcoin,monero,particl,bitcoincash,pivx,firo,dash,litecoin,dogecoin,decred'; + 'bitcoin,monero,particl,bitcoincash,pivx,firo,dash,litecoin,dogecoin,decred,namecoin'; //console.log('Fetching coin prices for:', coins); const response = await this.fetchCoinPrices(coins); @@ -254,7 +254,7 @@ const ApiManager = (function() { .filter(coin => coin.usesCoinGecko) .map(coin => getCoinBackendId ? getCoinBackendId(coin.name) : coin.name) .join(',') : - 'bitcoin,monero,particl,bitcoin-cash,pivx,firo,dash,litecoin,dogecoin,decred'; + 'bitcoin,monero,particl,bitcoin-cash,pivx,firo,dash,litecoin,dogecoin,decred,namecoin'; const url = `https://api.coingecko.com/api/v3/simple/price?ids=${coins}&vs_currencies=usd&include_24hr_vol=true&include_24hr_change=true`; diff --git a/basicswap/static/js/modules/cache-manager.js b/basicswap/static/js/modules/cache-manager.js index e8fba7c..1deddc8 100644 --- a/basicswap/static/js/modules/cache-manager.js +++ b/basicswap/static/js/modules/cache-manager.js @@ -504,13 +504,14 @@ const CacheManager = (function() { 'bitcoin': 'BTC', 'litecoin': 'LTC', 'monero': 'XMR', + 'wownero': 'WOW', 'particl': 'PART', 'pivx': 'PIVX', 'firo': 'FIRO', 'zcoin': 'FIRO', 'dash': 'DASH', 'decred': 'DCR', - 'wownero': 'WOW', + 'namecoin': 'NMR', 'bitcoin-cash': 'BCH', 'dogecoin': 'DOGE' }; diff --git a/basicswap/static/js/modules/config-manager.js b/basicswap/static/js/modules/config-manager.js index 1176cc3..0b70751 100644 --- a/basicswap/static/js/modules/config-manager.js +++ b/basicswap/static/js/modules/config-manager.js @@ -72,6 +72,7 @@ const ConfigManager = (function() { { symbol: 'LTC', name: 'litecoin', usesCryptoCompare: true, usesCoinGecko: true, historicalDays: 30 }, { symbol: 'DOGE', name: 'dogecoin', usesCryptoCompare: true, usesCoinGecko: true, historicalDays: 30 }, { symbol: 'DCR', name: 'decred', usesCryptoCompare: true, usesCoinGecko: true, historicalDays: 30 }, + { symbol: 'NMC', name: 'namecoin', usesCryptoCompare: true, usesCoinGecko: true, historicalDays: 30 }, { symbol: 'WOW', name: 'wownero', usesCryptoCompare: false, usesCoinGecko: true, historicalDays: 30 } ], @@ -88,6 +89,7 @@ const ConfigManager = (function() { 'Zcoin': 'FIRO', 'Dash': 'DASH', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Wownero': 'WOW', 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' @@ -105,13 +107,14 @@ const ConfigManager = (function() { 'Zcoin': 'Firo', 'Dash': 'Dash', 'Decred': 'Decred', + 'Namecoin': 'Namecoin', 'Wownero': 'Wownero', 'Bitcoin Cash': 'Bitcoin Cash', 'Dogecoin': 'Dogecoin' }, idToName: { - 1: 'particl', 2: 'bitcoin', 3: 'litecoin', 4: 'decred', + 1: 'particl', 2: 'bitcoin', 3: 'litecoin', 4: 'decred', 5: 'namecoin', 6: 'monero', 7: 'particl blind', 8: 'particl anon', 9: 'wownero', 11: 'pivx', 13: 'firo', 17: 'bitcoincash', 18: 'dogecoin' @@ -130,6 +133,7 @@ const ConfigManager = (function() { 'litecoin': 'litecoin', 'dogecoin': 'dogecoin', 'decred': 'decred', + 'namecoin': 'namecoin', 'wownero': 'wownero' } }, @@ -367,6 +371,7 @@ const ConfigManager = (function() { 'dash': { usd: null, btc: null }, 'dogecoin': { usd: null, btc: null }, 'decred': { usd: null, btc: null }, + 'namecoin': { usd: null, btc: null }, 'litecoin': { usd: null, btc: null }, 'particl': { usd: null, btc: null }, 'pivx': { usd: null, btc: null }, diff --git a/basicswap/static/js/modules/wallet-manager.js b/basicswap/static/js/modules/wallet-manager.js index b97f5b6..6b4b775 100644 --- a/basicswap/static/js/modules/wallet-manager.js +++ b/basicswap/static/js/modules/wallet-manager.js @@ -35,6 +35,7 @@ const WalletManager = (function() { 'Dash': 'DASH', 'PIVX': 'PIVX', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Bitcoin Cash': 'BCH' }, @@ -49,6 +50,7 @@ const WalletManager = (function() { 'DASH': 'dash', 'PIVX': 'pivx', 'DCR': 'dcr', + 'NMC': 'nmc', 'BCH': 'bch' }, @@ -63,6 +65,7 @@ const WalletManager = (function() { 'Dash': 'DASH', 'PIVX': 'PIVX', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' } diff --git a/basicswap/static/js/offers.js b/basicswap/static/js/offers.js index 1d534cf..355f3ec 100644 --- a/basicswap/static/js/offers.js +++ b/basicswap/static/js/offers.js @@ -34,6 +34,7 @@ window.tableRateModule = { 'Dash': 'DASH', 'PIVX': 'PIVX', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Zano': 'ZANO', 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' @@ -56,9 +57,9 @@ window.tableRateModule = { }, setCachedValue(key, value, resourceType = null) { - const ttl = resourceType ? - window.config.cacheConfig.ttlSettings[resourceType] || - window.config.cacheConfig.defaultTTL : + const ttl = resourceType ? + window.config.cacheConfig.ttlSettings[resourceType] || + window.config.cacheConfig.defaultTTL : 900000; const item = { @@ -306,13 +307,14 @@ async function calculateProfitLoss(fromCoin, toCoin, fromAmount, toAmount, isOwn 'ltc': 'litecoin', 'doge': 'dogecoin', 'dcr': 'decred', + 'nmc': 'namecoin', 'wow': 'wownero' }; if (lowerCoin === 'zcoin') return 'firo'; if (lowerCoin === 'bitcoin cash') return 'bitcoin-cash'; if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') return 'particl'; - + return symbolToName[lowerCoin] || lowerCoin; }; @@ -406,7 +408,7 @@ async function fetchLatestPrices() { const coinIds = [ 'bitcoin', 'particl', 'monero', 'litecoin', 'dogecoin', 'firo', 'dash', 'pivx', - 'decred', 'bitcoincash' + 'decred', 'namecoin', 'bitcoincash' ]; let processedData = {}; @@ -419,7 +421,7 @@ async function fetchLatestPrices() { if (mainResponse && mainResponse.rates) { Object.entries(mainResponse.rates).forEach(([coinId, price]) => { const normalizedCoinId = coinId === 'bitcoincash' ? 'bitcoin-cash' : coinId.toLowerCase(); - + processedData[normalizedCoinId] = { usd: price, btc: normalizedCoinId === 'bitcoin' ? 1 : price / (mainResponse.rates.bitcoin || 1) @@ -453,7 +455,7 @@ async function fetchLatestPrices() { } catch (error) { console.error(`Price fetch attempt ${attempt + 1} failed:`, error); NetworkManager.handleNetworkError(error); - + if (attempt < MAX_RETRIES - 1) { const delay = Math.min(500 * Math.pow(2, attempt), 5000); await new Promise(resolve => setTimeout(resolve, delay)); @@ -520,7 +522,7 @@ async function fetchOffers() { originalJsonData = [...jsonData]; latestPrices = pricesData || getEmptyPriceData(); - + CacheManager.set('offers_cached', jsonData, 'offers'); await updateOffersTable(); @@ -1353,7 +1355,7 @@ function createRateColumn(offer, coinFrom, coinTo) { const getPriceKey = (coin) => { const lowerCoin = coin.toLowerCase(); - + const symbolToName = { 'btc': 'bitcoin', 'xmr': 'monero', @@ -1365,13 +1367,14 @@ function createRateColumn(offer, coinFrom, coinTo) { 'ltc': 'litecoin', 'doge': 'dogecoin', 'dcr': 'decred', + 'nmc': 'namecoin', 'wow': 'wownero' }; - + if (lowerCoin === 'zcoin') return 'firo'; if (lowerCoin === 'bitcoin cash') return 'bitcoin-cash'; if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') return 'particl'; - + return symbolToName[lowerCoin] || lowerCoin; }; @@ -1655,23 +1658,24 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou 'ltc': 'litecoin', 'doge': 'dogecoin', 'dcr': 'decred', + 'nmc': 'namecoin', 'wow': 'wownero' }; if (lowerCoin === 'zcoin') return 'firo'; if (lowerCoin === 'bitcoin cash') return 'bitcoin-cash'; if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') return 'particl'; - + return symbolToName[lowerCoin] || lowerCoin; }; - + if (latestPrices && latestPrices['firo'] && !latestPrices['zcoin']) { latestPrices['zcoin'] = JSON.parse(JSON.stringify(latestPrices['firo'])); } const fromSymbol = getPriceKey(coinFrom); const toSymbol = getPriceKey(coinTo); - + let fromPriceUSD = latestPrices && latestPrices[fromSymbol] ? latestPrices[fromSymbol].usd : null; let toPriceUSD = latestPrices && latestPrices[toSymbol] ? latestPrices[toSymbol].usd : null; @@ -1685,7 +1689,7 @@ function createTooltipContent(isSentOffers, coinFrom, coinTo, fromAmount, toAmou isNaN(fromPriceUSD) || isNaN(toPriceUSD)) { return `

Price Information Unavailable

Current market prices are temporarily unavailable.

-

You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom} +

You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom} for ${toAmount.toFixed(8)} ${coinTo}.

Note:

Profit/loss calculations will be available when price data is restored.

`; @@ -1757,13 +1761,14 @@ function createCombinedRateTooltip(offer, coinFrom, coinTo, treatAsSentOffer) { 'ltc': 'litecoin', 'doge': 'dogecoin', 'dcr': 'decred', + 'nmc': 'namecoin', 'wow': 'wownero' }; if (lowerCoin === 'zcoin') return 'firo'; if (lowerCoin === 'bitcoin cash') return 'bitcoin-cash'; if (lowerCoin === 'particl anon' || lowerCoin === 'particl blind') return 'particl'; - + return symbolToName[lowerCoin] || lowerCoin; }; @@ -1876,7 +1881,7 @@ function clearFilters() { jsonData = [...originalJsonData]; currentPage = 1; - + const storageKey = isSentOffers ? 'sentOffersTableSettings' : 'networkOffersTableSettings'; localStorage.removeItem(storageKey); @@ -2194,7 +2199,7 @@ async function initializeTableAndData() { function loadSavedSettings() { const storageKey = isSentOffers ? 'sentOffersTableSettings' : 'networkOffersTableSettings'; const saved = localStorage.getItem(storageKey); - + if (saved) { const settings = JSON.parse(saved); @@ -2229,7 +2234,7 @@ document.addEventListener('DOMContentLoaded', async () => { NetworkManager.initialize({ connectionTestEndpoint: '/json', connectionTestTimeout: 3000, - reconnectDelay: 5000, + reconnectDelay: 5000, maxReconnectAttempts: 5 }); window.networkManagerInitialized = true; @@ -2252,7 +2257,7 @@ document.addEventListener('DOMContentLoaded', async () => { }); const tableLoadPromise = initializeTableAndData(); - + WebSocketManager.initialize({ debug: false }); @@ -2262,7 +2267,7 @@ document.addEventListener('DOMContentLoaded', async () => { if (!NetworkManager.isOnline()) { return; } - + const endpoint = isSentOffers ? '/json/sentoffers' : '/json/offers'; const response = await fetch(endpoint); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); @@ -2349,7 +2354,7 @@ async function cleanup() { lastRefreshTime = null; const domRefs = [ - 'offersBody', 'filterForm', 'prevPageButton', 'nextPageButton', + 'offersBody', 'filterForm', 'prevPageButton', 'nextPageButton', 'currentPageSpan', 'totalPagesSpan', 'lastRefreshTimeSpan', 'newEntriesCountSpan' ]; diff --git a/basicswap/static/js/pricechart.js b/basicswap/static/js/pricechart.js index 1e785d1..b8b818a 100644 --- a/basicswap/static/js/pricechart.js +++ b/basicswap/static/js/pricechart.js @@ -548,7 +548,7 @@ const ui = { }, setActiveContainer: (containerId) => { - const containerIds = ['btc', 'xmr', 'part', 'pivx', 'firo', 'dash', 'ltc', 'doge', 'eth', 'dcr', 'zano', 'wow', 'bch'].map(id => `${id}-container`); + const containerIds = ['btc', 'xmr', 'part', 'pivx', 'firo', 'dash', 'ltc', 'doge', 'eth', 'dcr', 'nmc', 'zano', 'wow', 'bch'].map(id => `${id}-container`); containerIds.forEach(id => { const container = document.getElementById(id); if (container) { diff --git a/basicswap/static/js/swaps_in_progress.js b/basicswap/static/js/swaps_in_progress.js index 3724bbd..8aeee3d 100644 --- a/basicswap/static/js/swaps_in_progress.js +++ b/basicswap/static/js/swaps_in_progress.js @@ -10,6 +10,7 @@ const COIN_NAME_TO_SYMBOL = { 'Firo': 'FIRO', 'Dash': 'DASH', 'Decred': 'DCR', + 'Namecoin': 'NMC', 'Wownero': 'WOW', 'Bitcoin Cash': 'BCH', 'Dogecoin': 'DOGE' @@ -383,8 +384,8 @@ const createSwapTableRow = async (swap) => {
- ${swap.coin_from} @@ -392,8 +393,8 @@ const createSwapTableRow = async (swap) => { - ${swap.coin_to} @@ -421,7 +422,7 @@ const createSwapTableRow = async (swap) => { - Details diff --git a/basicswap/templates/offers.html b/basicswap/templates/offers.html index eb04efb..7cb93b1 100644 --- a/basicswap/templates/offers.html +++ b/basicswap/templates/offers.html @@ -13,7 +13,7 @@
-
@@ -21,7 +21,7 @@

{{ page_type_description }}