Merge pull request from tecnovert/namecoin_v28

Namecoin v28
This commit is contained in:
tecnovert 2025-04-01 18:33:52 +00:00 committed by GitHub
commit aa9babdc69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 849 additions and 1012 deletions

View file

@ -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)

View file

@ -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,9 +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", "")
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", "")
@ -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",)),
@ -116,26 +119,32 @@ known_coins = {
disabled_coins = [
"navcoin",
"namecoin", # Needs update
]
expected_key_ids = {
"tecnovert": ("13F13651C9CF0D6B",),
"thrasher": ("FE3348877809386C",),
"laanwj": ("1E4AED62986CD25D",),
"JeremyRand": ("2DBE339E29F6294C",),
"binaryfate": ("F0AF4D462A0BDF92",),
"wowario": ("793504B449C69220",),
"davidburkett38": ("3620E9D387E55666",),
"xanimo": ("6E8F17C1B1BCDCBE",),
"patricklodder": ("2D3A345B98D0DC1F",),
"fuzzbawls": ("C1ABA64407731FD9",),
"pasta": ("52527BEDABE87984", "E2F3D7916E722D38"),
"reuben": ("1290A1D0FA7EE109",),
"nav_builder": ("2782262BF6E7FADB",),
"nicolasdorier": ("6618763EF09186FE", "223FDA69DEBEA82D", "62FE85647DEDDA2E"),
"decred_release": ("6D897EDF518A031D",),
"Calin_Culianu": ("21810A542031C02C",),
"tecnovert": ("8E517DC12EC1CC37F6423A8A13F13651C9CF0D6B",),
"thrasher": ("59CAF0E96F23F53747945FD4FE3348877809386C",),
"laanwj": ("9DEAE0DC7063249FB05474681E4AED62986CD25D",),
"RoseTuring": ("FD8366A807A99FA27FD9CCEA9FE3BFDDA6C53495",),
"binaryfate": ("81AC591FE9C4B65C5806AFC3F0AF4D462A0BDF92",),
"wowario": ("AB3A2F725818FCFF2794841C793504B449C69220",),
"davidburkett38": ("D35621D53A1CC6A3456758D03620E9D387E55666",),
"xanimo": ("2EAA8B1021C71AD5186CA07F6E8F17C1B1BCDCBE",),
"patricklodder": ("DC6EF4A8BF9F1B1E4DE1EE522D3A345B98D0DC1F",),
"fuzzbawls": ("0CFBDA9F60D661BA31EB5D50C1ABA64407731FD9",),
"pasta": (
"29590362EC878A81FD3C202B52527BEDABE87984",
"02B8E7D002167C8B451AF05FE2F3D7916E722D38",
),
"reuben": ("0186454D63E83D85EF91DE4E1290A1D0FA7EE109",),
"nav_builder": ("1BF9B51BAED51BA0B3A174EE2782262BF6E7FADB",),
"nicolasdorier": (
"AB4CFA9895ACA0DBE27F6B346618763EF09186FE",
"015B4C837B245509E4AC8995223FDA69DEBEA82D",
"7121BDE3555D9BE06BDDC68162FE85647DEDDA2E",
),
"decred_release": ("F516ADB7A069852C7C28A02D6D897EDF518A031D",),
"Calin_Culianu": ("D465135F97D0047E18E99DC321810A542031C02C",),
}
USE_PLATFORM = os.getenv("USE_PLATFORM", platform.system())
@ -181,6 +190,36 @@ 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_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", "")
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))
@ -203,32 +242,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))
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
@ -375,6 +388,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
@ -528,7 +547,7 @@ def testOnionLink():
def havePubkey(gpg, key_id):
for key in gpg.list_keys():
if key["keyid"] == key_id:
if key["fingerprint"] == key_id:
return True
return False
@ -591,8 +610,10 @@ def ensureValidSignatureBy(result, signing_key_name):
if not isValidSignature(result):
raise ValueError("Signature verification failed.")
if result.key_id not in expected_key_ids[signing_key_name]:
raise ValueError("Signature made by unexpected keyid: " + result.key_id)
if result.fingerprint not in expected_key_ids[signing_key_name]:
raise ValueError(
"Signature made by unexpected key fingerprint: " + result.fingerprint
)
logger.debug(f"Found valid signature by {signing_key_name} ({result.key_id}).")
@ -931,16 +952,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://www.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 = (
@ -1345,6 +1360,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)
@ -1401,6 +1418,10 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
)
elif coin == "namecoin":
fp.write("prune=2000\n")
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)
@ -1804,6 +1825,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
@ -1909,7 +1931,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)
@ -1926,11 +1948,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,
"",
@ -2073,7 +2099,10 @@ def check_btc_fastsync_data(base_dir, sync_filename):
importPubkey(gpg, pubkey_filename, pubkeyurls)
with open(asc_file_path, "rb") as fp:
verified = gpg.verify_file(fp)
if isValidSignature(verified) and verified.key_id in expected_key_ids["tecnovert"]:
if (
isValidSignature(verified)
and verified.fingerprint in expected_key_ids["tecnovert"]
):
ensureValidSignatureBy(verified, "tecnovert")
else:
pubkey_filename = "nicolasdorier.asc"
@ -2379,22 +2408,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"),
@ -2410,22 +2423,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"),
@ -2453,14 +2450,16 @@ 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,
"port": NMC_PORT + port_offset,
"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": {
@ -2486,6 +2485,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"),
@ -2549,27 +2570,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
},
}
@ -2593,9 +2624,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

View file

@ -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

View file

@ -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: {
@ -251,29 +257,38 @@ chainparams = {
"rpcport": 8336,
"pubkey_address": 52,
"script_address": 13,
"key_prefix": 180,
"hrp": "nc",
"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,
"pubkey_address": 111,
"script_address": 196,
"key_prefix": 239,
"hrp": "tn",
"bip44": 1,
"min_amount": 100000,
"max_amount": 10000000 * COIN,
"name": "testnet3",
"ext_public_key_prefix": 0x043587CF,
"ext_secret_key_prefix": 0x04358394,
},
"regtest": {
"rpcport": 18443,
"pubkey_address": 111,
"script_address": 196,
"key_prefix": 239,
"hrp": "ncrt",
"bip44": 1,
"min_amount": 100000,
"max_amount": 10000000 * COIN,
"ext_public_key_prefix": 0x043587CF,
"ext_secret_key_prefix": 0x04358394,
},
},
Coins.XMR: {

View file

@ -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"))
)

View file

@ -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)

View file

@ -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.
@ -13,39 +14,3 @@ class NMCInterface(BTCInterface):
@staticmethod
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
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

View file

@ -0,0 +1,25 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQMuBFmZ6L4RCACuqDDCIe2bzKznyKVN1aInzRQnSxdGTXuw0mcDz5HYudAhBjR8
gY6sxCRPNxvZCJVDZDpCygXMhWZlJtWLR8KMTCXxC4HLXXOY4RxQ5KGnYWxEAcKY
deq1ymmuOuMUp7ltRTSyWcBKbR9xTd2vW/+0W7GQIOxUW/aiT1V0x3cky+6kqaec
BorP3+uxJcx0Q8WdlS/6N4x3pBv/lfsdrZSaDD8fU/29pQGMDUEnupKoWJVVei6r
G+vxLHEtIFYYO8VWjZntymw3dl+aogrjyuxqWzl8mfPi9M/DgiRb4pJnH2yOGDI6
Lvg+oo9E79Vwi98UjYSicsB1dtcptKiA96UXAQD/hDB+dil7/SX/SDTlaw/+uTdd
Xg0No63dbN++iY4k3Qf/Xk1ZzbuDviLhe+zEhlJOw6TaMlxfwwQOtxEJXILS5uIL
jYlGcDbBtJh3p4qUoUduDOgjumJ9m47XqIq81rQ0pqzzGMbK1Y82NQjX5Sn8yTm9
p1hmOZ/uX9vCrUSbYBjxJXyQ1OXlerlLRLfBf5WQ0+LO+0cmgtCyX0zV4oGK7vph
XEm7lar7AezOOXaSrWAB+CTPUdJF1E7lcJiUuMVcqMx8pphrH+rfcsqPtN6tkyUD
TmPDpc5ViqFFelEEQnKSlmAY+3iCNZ3y/VdPPhuJ2lAsL3tm9MMh2JGV378LG45a
6SOkQrC977Qq1dhgJA+PGJxQvL2RJWsYlJwp79+Npgf9EfFaJVNzbdjGVq1XmNie
MZYqHRfABkyK0ooDxSyzJrq4vvuhWKInS4JhpKSabgNSsNiiaoDR+YYMHb0H8GRR
Za6JCmfU8w97R41UTI32N7dhul4xCDs5OV6maOIoNts20oigNGb7TKH9b5N7sDJB
zh3Of/fHCChO9Y2chbzU0bERfcn+evrWBf/9XdQGQ3ggoLbOtGpcUQuB/7ofTcBZ
awL6K4VJ2Qlb8DPlRgju6uU9AR/KTYeAlVFC8FX7R0FGgPRcJ3GNkNHGqrbuQ72q
AOhYOPx9nRrU5u+E2J325vOabLnLbOazze3j6LFPSFV4vfmTO9exYlwhz3g+lFAd
CrQ2Q2FsaW4gQ3VsaWFudSAoTmlsYWNUaGVHcmltKSA8Y2FsaW4uY3VsaWFudUBn
bWFpbC5jb20+iHoEExEIACIFAlmZ6L4CGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4B
AheAAAoJECGBClQgMcAsU5cBAO/ngONpHsxny2uTV4ge2f+5V2ajTjcIfN2jUZtg
31jJAQCl1NcrwcIu98+aM2IyjB1UFXkoaMANpr8L9jBopivRGQ==
=cf8I
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -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-----

View file

@ -0,0 +1,52 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
=atlH
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -1,78 +1,29 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
XHovfCfpcB/C
=0Wkp
mQINBFlLyYIBEADW3oJnVMDC94g+7OB1/IBUYNevCqJIOAtC0eeFS5g0XGvvqRLx
2NLUqn5te+R7deoGElkZFJLLxFUwEnhqGCRH50Iou5aanUzvgI5fVAbK3k0fp9vc
LKCR0fQVIidcLyqMpkLZo8BSE3+BWxFp/r2OHvh2dYtJC+BZVwblkDS3cqwKvUZx
IocvDs47Wo3tzZfEsqUavbbiGx+Dm0fCV7TVHdVLU7q3bZsHSRiyTUZ2EAApoAmT
ir9csVxv2IM8yf6/HwWi6/Lp7dgSG1+qBZ1lUPPTY+dFLPZyt/kul+vuOj6GLZaU
s3D66d7TaPCHKWAOnP9RHpic/iXODXVXo1KHJfa0x8fW7I+y7/Gb+5x/m4O0Bz2T
BivdrSAuFpXkPqwawlw4CPgI9fc801g83+ZFzD2jJ6qxkEgfnlmf+zGNn5tC4N5j
NRTQ+GyHo1w4824SXcSN590wgz8goGJC3QPJxbifvOA8GzQIVzpxHckofOVyqIEq
qSnkP2xn4mELqD7HcFnoojZBqFbF2cN+oWQ+niLN+v4qrUncpQI9SVWlyp66S+1T
BhBQj2QuX+3B+K27EiDbhNV7EX6xEbGsnB1poMc2aMiz6veybW3GnoM+2ppr8Ko/
12Ij7l+ZA44t6PWUfQQbNSbUk/0Yhd9QJ8VQVck+TaS6gtarTbURlSdmHwARAQAB
tCl0ZWNub3ZlcnQgKGdpdGlhbikgPHRlY25vdmVydEBwYXJ0aWNsLmlvPokCVAQT
AQgAPhYhBI5RfcEuwcw39kI6ihPxNlHJzw1rBQJZS8mCAhsDBQkSzAMABQsJCAcC
BhUICQoLAgQWAgMBAh4BAheAAAoJEBPxNlHJzw1rtdYP/22iRX8O2Q++MayPHNx5
XHAlMk9mfi5FB1qJwshtlhda7P9U/hOTi227wH+Mzh5dBje4t2DkoHzxlz/Wr4cQ
QUJMOYd0OEZY6kpAQkvtyYobIb6zlRQK1koAfNMxewmfZZGTlr16IUVCovGSFvZ+
hdYRDEjuHqXjpwBfrxFAy/HCnfY10qSRkJc5w1ypj1IkzlanS+xeRJSDvRTQDAEr
zv3xKcMGjCCHaaCP+tyAaViBaUOlvmZdWwg0gwQCuPLqIh0cfDbcg0quciRIpnyp
zINmfwngZCwXdIYfAzmCzMHw1J3iOiqfqK0EcpHMsL689VyQSPgsoEHtcOGHYjRL
pMPGvRFHtICrnCHENK3IcwFWDGXW+i3zgOlA7g48yYWWvSup+t8I6YT+FeeFlxSO
dj1GdeMA0O7gXZ7znLVduokL2Ef4dZjc+3NwBlFov52vwCZwQMAGsMriwEDB2rDZ
B7YOvAxlUB/kavtx/oE8fV7mZcuwYg02lq4bozF9xlhjOFaRit6xXnLVi0TuI76c
uz67nB9VkWczSLIzCyjNyFpWbx1BMxTYfehZX3+YNajXwG6HdEp9CAYK0u46Guz/
Pth67bbNVYyP/cuOIrv/hqQ6xo4mOBMDDxcCEAXx3rwxfbxNM8vlwrMcpITrtNON
r41bcxUIfMEDefPm5wnXep8W
=szpX
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1,29 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGROehcBEACXWWc6dHqCos1PmKI32iHi0jP3mYM3jU57YxbjwT78QEtEwSqf
YklpXkgTYq7jexx2JElfegM6w1sPYarq1y051RjnCgzl32da5I506SMvcJTmXumV
Rw6erPeDxAO74PflDSlALgtGOgbKhwwWRudbWgT5hKGkl62qy0mI6GStul0rbT+3
gq77DCGyURfe1PG1pymhO5XVz3WGtOa12NvRA+3wGIcqIji2MbtXuOhGMg//kVI5
m2vcfHyMMuQ01xUXRu57WxRujYaJ1RB4p86JCbDX3YU2XlzTxGAhqChDLuJGqo54
AZMUWDceftXsAoOqH8Hwmm5gFkYSpMt86ZT+umvWygmxohD5k85MuRj4AGagFj/u
CMcQjI/SN1UU/Qozg6VL/5FO8aH9IybDzX7eE3j0V/jTweStw1CIUajYgfemWOWl
whLPBDflRz/8EEqTN0CaSSaiYiULZUiawBO/bRIiCO2Q6QrAi3KpPUhCwiw/Yecd
rAMLH7bytpECDdbNonQ/VMxWwtWJQ87qBtWvHFQxXBKjyuANsKL9X7v3KcYOUdd2
fSt7eqE9GDT4DbK6sTmuTpq2TgHXET0cA39+N2zxTh5xFupI/pi2iAHJ6hgIiQnn
662TngjGOSFvrTV/51Ua0Vx8OCMJJOcRdOVaYzuzg9DsjVcJin3aRqUh4wARAQAB
tCBXb3dhcmlvIDx3b3dhcmlvQHByb3Rvbm1haWwuY29tPokCVAQTAQgAPhYhBKs6
L3JYGPz/J5SEHHk1BLRJxpIgBQJkTnoXAhsDBQkHhh77BQsJCAcCBhUKCQgLAgQW
AgMBAh4BAheAAAoJEHk1BLRJxpIgwgEP/109vw1WXRh9EaRr3Y1+sBi+/PRQ5TCx
UEcP9ru5sQPJ0BsZK8RYw0BNIfDQX9OB1k/AoiBelL+0EoDKvjXmwz9fPUmSVk5r
3RzfClXTnxn4HXPKkSGMt4WBUnvohTexK7CPkb9xy+K0Jtx8XF1XiQLDFg2a9lBj
IIX2H6aHn4VjdUBv7TrTCAI2Vg0cQUpeJUwyHH+lk0r2WM3zAxzS3Iy2yDDstNT8
audXEX4BtJhyEU1m57jwgscrbTtgwYOAsaRLcnUaAFWhbov3IiGInk7N1fkMsuW5
HE5RcegSZRS3X4o6O/nmwdSjCEB9weydOCPrtfdbvfvuTiMg/jZBikOk/Sj7FM/D
eZKghSHpLbT/V3S76FyIcc/xFkUmR+2fGvCNjJ1Qn2lXTS8xcbyzqR4LZPeUGppV
hvriilLnXSjyc60wuD3kmCCo1Zw4tNL8pr09BtVmScUy6eiwca8LLzvbbivqxF1g
Mrkkv8yQE0ZwO1kgNSn+PSzUPbwAoklcyN5Rhr5DxZh0UudiH5Jt5WWYeE8O2Uc1
si13X575kymGkkeiUcp9WtBkh2uial+RVmTrUTDUTIR2HzT6MAR84/DHlC5dsW8a
h4uDUhzeG2cTxuIfZC881UHKL+xT/I3PPuFdLbU5uoWJpXYpxKYulYWd7LA/k4bi
JWBrQo7VDvvP
=H3wS
-----END PGP PUBLIC KEY BLOCK-----

Binary file not shown.

After

(image error) Size: 743 B

Binary file not shown.

After

(image error) Size: 4.2 KiB

View file

@ -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) => {
</a>
</div>
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
<span class="font-semibold">Offer ID:</span>
<span class="font-semibold">Offer ID:</span>
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
${formatAddress(bid.offer_id)}
</a>
</div>
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
<span class="font-semibold">Bid ID:</span>
<span class="font-semibold">Bid ID:</span>
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
${formatAddress(bid.bid_id)}
</a>
@ -366,8 +367,8 @@ const createBidTableRow = async (bid) => {
<div class="py-3 px-4 text-center">
<div class="flex items-center justify-center">
<span class="inline-flex mr-3 align-middle items-center justify-center w-18 h-20 rounded">
<img class="h-12"
src="/static/images/coins/${bid.coin_from.replace(' ', '-')}.png"
<img class="h-12"
src="/static/images/coins/${bid.coin_from.replace(' ', '-')}.png"
alt="${bid.coin_from}"
onerror="this.src='/static/images/coins/default.png'">
</span>
@ -375,15 +376,14 @@ const createBidTableRow = async (bid) => {
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"></path>
</svg>
<span class="inline-flex ml-3 align-middle items-center justify-center w-18 h-20 rounded">
<img class="h-12"
src="/static/images/coins/${bid.coin_to.replace(' ', '-')}.png"
<img class="h-12"
src="/static/images/coins/${bid.coin_to.replace(' ', '-')}.png"
alt="${bid.coin_to}"
onerror="this.src='/static/images/coins/default.png'">
</span>
</div>
</div>
</td>
<!-- You Get Column -->
<td class="py-0">
<div class="py-3 px-4 text-right">
@ -410,7 +410,7 @@ const createBidTableRow = async (bid) => {
<!-- Actions Column -->
<td class="py-3 px-4 text-center">
<a href="/bid/${bid.bid_id}/accept"
<a href="/bid/${bid.bid_id}/accept"
class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200">
Accept
</a>
@ -506,13 +506,13 @@ const createDetailsColumn = (bid, identity, uniqueId) => `
</a>
</div>
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
<span class="font-semibold">Offer ID:</span>
<span class="font-semibold">Offer ID:</span>
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
${formatAddress(bid.offer_id)}
</a>
</div>
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
<span class="font-semibold">Bid ID:</span>
<span class="font-semibold">Bid ID:</span>
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
${formatAddress(bid.bid_id)}
</a>

View file

@ -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`;

View file

@ -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'
};

View file

@ -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 },

View file

@ -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'
}

View file

@ -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 `<p class="font-bold mb-1">Price Information Unavailable</p>
<p>Current market prices are temporarily unavailable.</p>
<p class="mt-2">You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom}
<p class="mt-2">You are ${isSentOffers ? 'selling' : 'buying'} ${fromAmount.toFixed(8)} ${coinFrom}
for ${toAmount.toFixed(8)} ${coinTo}.</p>
<p class="font-bold mt-2">Note:</p>
<p>Profit/loss calculations will be available when price data is restored.</p>`;
@ -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'
];

View file

@ -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) {

View file

@ -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) => {
<div class="py-3 px-4 text-center">
<div class="flex items-center justify-center">
<span class="inline-flex mr-3 align-middle items-center justify-center w-18 h-20 rounded">
<img class="h-12"
src="/static/images/coins/${swap.coin_from.replace(' ', '-')}.png"
<img class="h-12"
src="/static/images/coins/${swap.coin_from.replace(' ', '-')}.png"
alt="${swap.coin_from}"
onerror="this.src='/static/images/coins/default.png'">
</span>
@ -392,8 +393,8 @@ const createSwapTableRow = async (swap) => {
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"></path>
</svg>
<span class="inline-flex ml-3 align-middle items-center justify-center w-18 h-20 rounded">
<img class="h-12"
src="/static/images/coins/${swap.coin_to.replace(' ', '-')}.png"
<img class="h-12"
src="/static/images/coins/${swap.coin_to.replace(' ', '-')}.png"
alt="${swap.coin_to}"
onerror="this.src='/static/images/coins/default.png'">
</span>
@ -421,7 +422,7 @@ const createSwapTableRow = async (swap) => {
<!-- Actions Column -->
<td class="py-3 px-4 text-center">
<a href="/bid/${swap.bid_id}"
<a href="/bid/${swap.bid_id}"
class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200">
Details
</a>

View file

@ -13,7 +13,7 @@
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover"
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover"
src="/static/images/elements/wave.svg" alt="">
<div class="relative z-20 flex flex-wrap items-center -m-3">
<div class="w-full md:w-1/2 p-3">
@ -21,7 +21,7 @@
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
</div>
<div class="w-full md:w-1/2 p-3 flex justify-end items-center hidden">
<a id="refresh" href="/newoffer"
<a id="refresh" href="/newoffer"
class="rounded-full flex items-center justify-center px-4 py-2 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">
{{ place_new_offer_svg | safe }}
<span>Place new Offer</span>
@ -136,12 +136,13 @@
'ETH': {'name': 'Ethereum', 'symbol': 'ETH', 'image': 'Ethereum.png', 'show': false},
'DOGE': {'name': 'Dogecoin', 'symbol': 'DOGE', 'image': 'Dogecoin.png', 'show': true},
'DCR': {'name': 'Decred', 'symbol': 'DCR', 'image': 'Decred.png', 'show': true},
'NMC': {'name': 'Namecoin', 'symbol': 'NMC', 'image': 'Namecoin.png', 'show': true},
'ZANO': {'name': 'Zano', 'symbol': 'ZANO', 'image': 'Zano.png', 'show': false},
'WOW': {'name': 'Wownero', 'symbol': 'WOW', 'image': 'Wownero.png', 'show': true}
}
%}
{% set custom_order = ['BTC', 'ETH', 'XMR', 'PART', 'LTC', 'BCH', 'FIRO', 'PIVX', 'DASH', 'DOGE', 'DCR', 'ZANO', 'WOW'] %}
{% set custom_order = ['BTC', 'ETH', 'XMR', 'PART', 'LTC', 'BCH', 'FIRO', 'PIVX', 'DASH', 'DOGE', 'DCR', 'NMC', 'ZANO', 'WOW'] %}
{% if enabled_chart_coins is string %}
{% if enabled_chart_coins == "" %}
{% set display_coins = coin_data.keys()|list %}

View file

@ -34,6 +34,11 @@ from tests.basicswap.common import (
PIVX_BASE_PORT,
BTC_USE_DESCRIPTORS,
)
from tests.basicswap.extended.test_nmc import (
NMC_BASE_PORT,
NMC_BASE_RPC_PORT,
NMC_BASE_TOR_PORT,
)
from tests.basicswap.extended.test_dcr import (
DCR_BASE_PORT,
DCR_BASE_RPC_PORT,
@ -50,13 +55,6 @@ from tests.basicswap.extended.test_doge import (
import basicswap.config as cfg
import basicswap.bin.run as runSystem
XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 29798
XMR_BASE_WALLET_RPC_PORT = 29998
FIRO_BASE_PORT = 34832
FIRO_BASE_RPC_PORT = 35832
FIRO_RPC_PORT_BASE = int(os.getenv("FIRO_RPC_PORT_BASE", FIRO_BASE_RPC_PORT))
TEST_PATH = os.path.expanduser(os.getenv("TEST_PATH", "~/test_basicswap1"))
@ -68,7 +66,21 @@ BITCOIN_RPC_PORT_BASE = int(os.getenv("BITCOIN_RPC_PORT_BASE", BTC_BASE_RPC_PORT
BITCOIN_TOR_PORT_BASE = int(os.getenv("BITCOIN_TOR_PORT_BASE", BTC_BASE_TOR_PORT))
LITECOIN_RPC_PORT_BASE = int(os.getenv("LITECOIN_RPC_PORT_BASE", LTC_BASE_RPC_PORT))
DECRED_RPC_PORT_BASE = int(os.getenv("DECRED_RPC_PORT_BASE", DCR_BASE_RPC_PORT))
NAMECOIN_PORT_BASE = int(os.getenv("NAMECOIN_PORT_BASE", NMC_BASE_PORT))
NAMECOIN_RPC_PORT_BASE = int(os.getenv("NAMECOIN_RPC_PORT_BASE", NMC_BASE_RPC_PORT))
NAMECOIN_TOR_PORT_BASE = int(os.getenv("NAMECOIN_TOR_PORT_BASE", NMC_BASE_TOR_PORT))
XMR_BASE_P2P_PORT = 17792
XMR_BASE_RPC_PORT = 29798
XMR_BASE_WALLET_RPC_PORT = 29998
FIRO_BASE_PORT = 34832
FIRO_BASE_RPC_PORT = 35832
FIRO_RPC_PORT_BASE = int(os.getenv("FIRO_RPC_PORT_BASE", FIRO_BASE_RPC_PORT))
BITCOINCASH_RPC_PORT_BASE = int(
os.getenv("BITCOINCASH_RPC_PORT_BASE", BCH_BASE_RPC_PORT)
)
@ -130,19 +142,21 @@ def run_prepare(
os.environ["BTC_RPC_PORT"] = str(BITCOIN_RPC_PORT_BASE)
os.environ["BTC_PORT"] = str(BITCOIN_PORT_BASE)
os.environ["BTC_USE_DESCRIPTORS"] = str(BTC_USE_DESCRIPTORS)
os.environ["BTC_ONION_PORT"] = str(BITCOIN_TOR_PORT_BASE)
os.environ["LTC_RPC_PORT"] = str(LITECOIN_RPC_PORT_BASE)
os.environ["DCR_RPC_PORT"] = str(DECRED_RPC_PORT_BASE)
os.environ["DCR_RPC_PWD"] = "dcr_pwd"
os.environ["NMC_RPC_PORT"] = str(NAMECOIN_RPC_PORT_BASE)
os.environ["NMC_PORT"] = str(NMC_BASE_PORT)
os.environ["NMC_ONION_PORT"] = str(NAMECOIN_TOR_PORT_BASE)
os.environ["XMR_RPC_USER"] = "xmr_user"
os.environ["XMR_RPC_PWD"] = "xmr_pwd"
os.environ["FIRO_RPC_PORT"] = str(FIRO_RPC_PORT_BASE)
os.environ["BCH_PORT"] = str(BCH_BASE_PORT)
os.environ["BCH_RPC_PORT"] = str(BITCOINCASH_RPC_PORT_BASE)
os.environ["DOGE_PORT"] = str(DOGE_BASE_PORT)
os.environ["DOGE_RPC_PORT"] = str(DOGECOIN_RPC_PORT_BASE)
os.environ["XMR_RPC_USER"] = "xmr_user"
os.environ["XMR_RPC_PWD"] = "xmr_pwd"
os.environ["DCR_RPC_PWD"] = "dcr_pwd"
import basicswap.bin.prepare as prepareSystem
# Hack: Reload module to set env vars as the basicswap_prepare module is initialised if imported from elsewhere earlier
@ -320,6 +334,62 @@ def run_prepare(
with open(config_filename, "a") as fp:
fp.write("enablevoting=1\n")
if "namecoin" in coins_array:
# Pruned nodes don't provide blocks
config_filename = os.path.join(datadir_path, "namecoin", "namecoin.conf")
with open(config_filename, "r") as fp:
lines = fp.readlines()
with open(config_filename, "w") as fp:
for line in lines:
if not line.startswith("prune"):
fp.write(line)
# fp.write("bind=127.0.0.1\n") # Causes BTC v28 to try and bind to bind=127.0.0.1:8444, even with a bind...=onion present
# listenonion=0 does not stop the node from trying to bind to the tor port
# https://github.com/bitcoin/bitcoin/issues/22726
fp.write(
"bind=127.0.0.1:{}=onion\n".format(
NAMECOIN_TOR_PORT_BASE + node_id + port_ofs
)
)
fp.write("dnsseed=0\n")
fp.write("discover=0\n")
fp.write("listenonion=0\n")
fp.write("upnp=0\n")
if use_rpcauth:
salt = generate_salt(16)
rpc_user = "test_nmc_" + str(node_id)
rpc_pass = "test_nmc_pwd_" + str(node_id)
fp.write(
"rpcauth={}:{}${}\n".format(
rpc_user, salt, password_to_hmac(salt, rpc_pass)
)
)
settings["chainclients"]["namecoin"]["rpcuser"] = rpc_user
settings["chainclients"]["namecoin"]["rpcpassword"] = rpc_pass
for ip in range(num_nodes):
if ip != node_id:
fp.write(
"connect=127.0.0.1:{}\n".format(
NAMECOIN_PORT_BASE + ip + port_ofs
)
)
for opt in EXTRA_CONFIG_JSON.get("ncm{}".format(node_id), []):
fp.write(opt + "\n")
if "monero" in coins_array:
with open(os.path.join(datadir_path, "monero", "monerod.conf"), "a") as fp:
fp.write("p2p-bind-ip=127.0.0.1\n")
fp.write(
"p2p-bind-port={}\n".format(XMR_BASE_P2P_PORT + node_id + port_ofs)
)
for ip in range(num_nodes):
if ip != node_id:
fp.write(
"add-exclusive-node=127.0.0.1:{}\n".format(
XMR_BASE_P2P_PORT + ip + port_ofs
)
)
if "pivx" in coins_array:
# Pruned nodes don't provide blocks
config_filename = os.path.join(datadir_path, "pivx", "pivx.conf")
@ -388,20 +458,6 @@ def run_prepare(
for opt in EXTRA_CONFIG_JSON.get("firo{}".format(node_id), []):
fp.write(opt + "\n")
if "monero" in coins_array:
with open(os.path.join(datadir_path, "monero", "monerod.conf"), "a") as fp:
fp.write("p2p-bind-ip=127.0.0.1\n")
fp.write(
"p2p-bind-port={}\n".format(XMR_BASE_P2P_PORT + node_id + port_ofs)
)
for ip in range(num_nodes):
if ip != node_id:
fp.write(
"add-exclusive-node=127.0.0.1:{}\n".format(
XMR_BASE_P2P_PORT + ip + port_ofs
)
)
if "bitcoincash" in coins_array:
config_filename = os.path.join(datadir_path, "bitcoincash", "bitcoin.conf")
with open(config_filename, "r") as fp:

View file

@ -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",

View file

@ -11,78 +11,58 @@ 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,
)
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,
toBool,
)
from basicswap.bin.run import startDaemon
from basicswap.contrib.rpcauth import generate_salt, password_to_hmac
from tests.basicswap.common import (
stopDaemons,
waitForRPC,
make_rpc_func,
)
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))
NUM_NODES = 3
NMC_NODE = 3
BTC_NODE = 4
delay_event = threading.Event()
stop_test = False
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_USE_DESCRIPTORS = toBool(os.getenv("NMC_USE_DESCRIPTORS", True))
NMC_BASE_PORT = 8136
NMC_BASE_RPC_PORT = 8146
NMC_BASE_TOR_PORT = 8156
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)
@ -90,8 +70,17 @@ 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")
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")
@ -99,595 +88,182 @@ 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")
if conf_file == "bitcoin.conf":
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("deprecatedrpc=create_bdb\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": BASE_RPC_PORT + NMC_NODE,
"datadir": nmcdatadir,
"bindir": cfg.NAMECOIN_BINDIR,
"use_csv": False,
# '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 = 100000 # 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(
cfg.NAMECOIN_BINDIR,
os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)),
"regtest",
cmd,
cfg.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):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass()
eckey = ECKey()
eckey.generate()
cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes())
cls.network_pubkey = eckey.get_pubkey().get_bytes().hex()
if os.path.isdir(cfg.TEST_DATADIRS):
logging.info("Removing " + cfg.TEST_DATADIRS)
shutil.rmtree(cfg.TEST_DATADIRS)
for i in range(NUM_NODES):
prepareDir(cfg.TEST_DATADIRS, i, cls.network_key, cls.network_pubkey)
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(
startDaemon(
os.path.join(cfg.TEST_DATADIRS, str(NMC_NODE)),
cfg.NAMECOIN_BINDIR,
cfg.NAMECOIND,
)
)
logging.info("Started %s %d", cfg.NAMECOIND, cls.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('"', '\\"'),
],
)
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()
waitForRPC(nmcRpc, delay_event)
num_blocks = 500
logging.info("Mining %d namecoin blocks", num_blocks)
cls.nmc_addr = nmcRpc("getnewaddress mining_addr legacy")
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
def mineBlock(self, num_blocks: int = 1) -> None:
self.callnoderpc("generatetoaddress", [num_blocks, self.nmc_addr])
@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()
logging.info("Finalising Namecoin Test")
stopDaemons(cls.nmc_daemons)
cls.nmc_daemons.clear()
stopDaemons(cls.daemons)
cls.http_threads.clear()
cls.swap_clients.clear()
cls.daemons.clear()
super(TestNMC, cls).tearDownClass()
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,
TxLockTypes.ABS_LOCK_TIME,
)
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,
TxLockTypes.ABS_LOCK_TIME,
)
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,
TxLockTypes.ABS_LOCK_TIME,
)
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.ABS_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,
TxLockTypes.ABS_LOCK_TIME,
)
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.001 * COIN,
1.0 * COIN,
0.001 * COIN,
SwapTypes.SELLER_FIRST,
TxLockTypes.ABS_LOCK_TIME,
)
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)
@classmethod
def coins_loop(cls):
super(TestNMC, cls).coins_loop()
ci0 = cls.swap_clients[0].ci(cls.test_coin)
try:
swap_clients[0].getChainClientSettings(Coins.BTC)["override_feerate"] = 10.0
swap_clients[0].getChainClientSettings(Coins.NMC)["override_feerate"] = 10.0
wait_for_bid(
delay_event, swap_clients[0], bid_id, BidStates.BID_ERROR, wait_for=60
)
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"]
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}")
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
@classmethod
def prepareExtraDataDir(cls, i: int) -> None:
if not cls.restore_instance:
prepareNMCDataDir(cfg.TEST_DATADIRS, i)
cls.nmc_daemons.append(
startDaemon(
os.path.join(cfg.TEST_DATADIRS, "nmc_" + str(i)),
NAMECOIN_BINDIR,
NAMECOIND,
)
)
logging.info("Started {} {}".format(NAMECOIND, cls.nmc_daemons[-1].handle.pid))
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, True, "", False, NMC_USE_DESCRIPTORS],
)
if NMC_USE_DESCRIPTORS:
nmc_rpc(
"createwallet",
["bsx_watch", True, True, "", False, True],
)
@classmethod
def addPIDInfo(cls, sc, i):
sc.setDaemonPID(Coins.DCR, cls.nmc_daemons[i].handle.pid)
@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,
"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(
"getaddressesbylabel",
[
"mining_addr",
],
)
cls.nmc_addr = addrs.keys()[0]
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, NMC_USE_DESCRIPTORS],
)
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__":

View file

@ -60,6 +60,7 @@ from tests.basicswap.common_xmr import (
prepare_nodes,
XMR_BASE_RPC_PORT,
DOGE_BASE_RPC_PORT,
NMC_BASE_RPC_PORT,
)
from basicswap.interface.dcr.rpc import callrpc as callrpc_dcr
import basicswap.bin.run as runSystem
@ -73,12 +74,13 @@ UI_PORT = 12700 + PORT_OFS
PARTICL_RPC_PORT_BASE = int(os.getenv("PARTICL_RPC_PORT_BASE", BASE_RPC_PORT))
BITCOIN_RPC_PORT_BASE = int(os.getenv("BITCOIN_RPC_PORT_BASE", BTC_BASE_RPC_PORT))
LITECOIN_RPC_PORT_BASE = int(os.getenv("LITECOIN_RPC_PORT_BASE", LTC_BASE_RPC_PORT))
DOGECOIN_RPC_PORT_BASE = int(os.getenv("DOGECOIN_RPC_PORT_BASE", DOGE_BASE_RPC_PORT))
DECRED_WALLET_RPC_PORT_BASE = int(os.getenv("DECRED_WALLET_RPC_PORT_BASE", 9210))
NAMECOIN_RPC_PORT_BASE = int(os.getenv("NAMECOIN_RPC_PORT_BASE", NMC_BASE_RPC_PORT))
XMR_BASE_RPC_PORT = int(os.getenv("XMR_BASE_RPC_PORT", XMR_BASE_RPC_PORT))
BITCOINCASH_RPC_PORT_BASE = int(
os.getenv("BITCOINCASH_RPC_PORT_BASE", BCH_BASE_RPC_PORT)
)
DECRED_WALLET_RPC_PORT_BASE = int(os.getenv("DECRED_WALLET_RPC_PORT_BASE", 9210))
XMR_BASE_RPC_PORT = int(os.getenv("XMR_BASE_RPC_PORT", XMR_BASE_RPC_PORT))
DOGECOIN_RPC_PORT_BASE = int(os.getenv("DOGECOIN_RPC_PORT_BASE", DOGE_BASE_RPC_PORT))
TEST_COINS_LIST = os.getenv("TEST_COINS_LIST", "bitcoin,monero")
NUM_NODES = int(os.getenv("NUM_NODES", 3))
@ -130,6 +132,17 @@ def calldcrrpc(
return callrpc_dcr(base_rpc_port + node_id, auth, method, params)
def callnmcrpc(
node_id,
method,
params=[],
wallet="wallet.dat",
base_rpc_port=NAMECOIN_RPC_PORT_BASE + PORT_OFS,
):
auth = "test_nmc_{0}:test_nmc_pwd_{0}".format(node_id)
return callrpc(base_rpc_port + node_id, auth, method, params, wallet)
def callbchrpc(
node_id,
method,
@ -163,6 +176,8 @@ def updateThread(cls):
callbchrpc(0, "generatetoaddress", [1, cls.bch_addr])
if cls.doge_addr is not None:
calldogerpc(0, "generatetoaddress", [1, cls.doge_addr])
if cls.nmc_addr is not None:
callnmcrpc(0, "generatetoaddress", [1, cls.nmc_addr])
except Exception as e:
print("updateThread error", str(e))
cls.delay_event.wait(random.randrange(cls.update_min, cls.update_max))
@ -388,6 +403,18 @@ def start_processes(self):
0, "generatetoaddress", [num_blocks - have_blocks, self.doge_addr]
)
if "namecoin" in TEST_COINS_LIST:
self.nmc_addr = callnmcrpc(0, "getnewaddress", ["mining_addr", "bech32"])
num_blocks: int = 500
have_blocks: int = callnmcrpc(0, "getblockcount")
if have_blocks < num_blocks:
logging.info(
f"Mining {num_blocks - have_blocks} Namecoin blocks to {self.nmc_addr}"
)
callnmcrpc(
0, "generatetoaddress", [num_blocks - have_blocks, self.nmc_addr]
)
if RESET_TEST:
# Lower output split threshold for more stakeable outputs
for i in range(NUM_NODES):
@ -444,6 +471,7 @@ class BaseTestWithPrepare(unittest.TestCase):
dcr_addr = "SsYbXyjkKAEXXcGdFgr4u4bo4L8RkCxwQpH"
dcr_acc = None
doge_addr = None
nmc_addr = None
initialised = False

View file

@ -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 (
@ -59,7 +62,6 @@ from basicswap.contrib.test_framework.script import (
from .test_xmr import BaseTest, test_delay_event, callnoderpc
logger = logging.getLogger()
test_seed = "8e54a313e6df8918df6d758fafdbf127a115175fdd2238d0e908dd8093c9ac3b"
@ -183,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()
@ -666,7 +668,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 +720,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 +773,8 @@ class TestFunctions(BaseTest):
class BasicSwapTest(TestFunctions):
test_fee_rate: int = 1000 # sats/kvB
@classmethod
def setUpClass(cls):
super(BasicSwapTest, cls).setUpClass()
@ -1178,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)
@ -1236,12 +1245,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 +1270,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 +1296,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
@ -1323,10 +1317,7 @@ 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
lockedunspents_before = ci.rpc_wallet("listlockunspent")
a = ci.getNewRandomKey()
b = ci.getNewRandomKey()
@ -1335,17 +1326,26 @@ 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)
# 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"]
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 +1372,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 +1400,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 +1635,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 +1647,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 +1660,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 +1687,56 @@ 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,
)
# 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])