diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py index 3bee51d..0ef3919 100755 --- a/basicswap/bin/prepare.py +++ b/basicswap/bin/prepare.py @@ -1315,7 +1315,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): ) wallet_conf_path = os.path.join(data_dir, wallet_conf_filename) if os.path.exists(wallet_conf_path): - exitWithError("{} exists".format(wallet_conf_path)) + exitWithError(f"{wallet_conf_path} exists") with open(wallet_conf_path, "w") as fp: if chain != "mainnet": fp.write(chainname + "=1\n") @@ -1342,7 +1342,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): core_conf_name: str = core_settings.get("config_filename", coin + ".conf") core_conf_path = os.path.join(data_dir, core_conf_name) if os.path.exists(core_conf_path): - exitWithError("{} exists".format(core_conf_path)) + exitWithError(f"{core_conf_path} exists") with open(core_conf_path, "w") as fp: if chain != "mainnet": if coin in ("navcoin",): @@ -1393,6 +1393,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): ) elif coin == "litecoin": fp.write("prune=4000\n") + fp.write("changetype=bech32\n") if LTC_RPC_USER != "": fp.write( "rpcauth={}:{}${}\n".format( @@ -1410,6 +1411,7 @@ def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}): elif coin == "bitcoin": fp.write("deprecatedrpc=create_bdb\n") fp.write("prune=2000\n") + fp.write("changetype=bech32\n") fp.write("fallbackfee=0.0002\n") if BTC_RPC_USER != "": fp.write( @@ -1784,7 +1786,7 @@ def test_particl_encryption(data_dir, settings, chain, use_tor_proxy): coin_name = "particl" coin_settings = settings["chainclients"][coin_name] daemon_args += getCoreBinArgs(c, coin_settings, prepare=True) - extra_config = {"stdout_to_file": True} + extra_config = {"stdout_to_file": True, "coin_name": coin_name} if coin_settings["manage_daemon"]: filename: str = getCoreBinName(c, coin_settings, coin_name + "d") daemons.append( @@ -1906,7 +1908,7 @@ def initialise_wallets( ) ] - extra_config = {"stdout_to_file": True} + extra_config = {"stdout_to_file": True, "coin_name": coin_name} daemons.append( startDaemon( coin_settings["datadir"], diff --git a/basicswap/bin/run.py b/basicswap/bin/run.py index b1d7f72..819cd53 100755 --- a/basicswap/bin/run.py +++ b/basicswap/bin/run.py @@ -59,15 +59,20 @@ def signal_handler(sig, frame): def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}): daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin)) datadir_path = os.path.expanduser(node_dir) + coin_name = extra_config.get("coin_name", "") # Rewrite litecoin.conf # TODO: Remove - needs_rewrite: bool = False ltc_conf_path = os.path.join(datadir_path, "litecoin.conf") if os.path.exists(ltc_conf_path): + needs_rewrite: bool = False + add_changetype: bool = True with open(ltc_conf_path) as fp: for line in fp: line = line.strip() + if line.startswith("changetype="): + add_changetype = False + break if line.endswith("=onion"): needs_rewrite = True break @@ -83,6 +88,29 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}): fp_to.write(line.strip()[:-6] + "\n") else: fp_to.write(line) + if add_changetype: + fp_to.write("changetype=bech32\n") + add_changetype = False + if add_changetype: + logger.info("Adding changetype to litecoin.conf") + with open(ltc_conf_path, "a") as fp: + fp.write("changetype=bech32\n") + + # Rewrite bitcoin.conf + # TODO: Remove + btc_conf_path = os.path.join(datadir_path, "bitcoin.conf") + if coin_name == "bitcoin" and os.path.exists(btc_conf_path): + add_changetype: bool = True + with open(btc_conf_path) as fp: + for line in fp: + line = line.strip() + if line.startswith("changetype="): + add_changetype = False + break + if add_changetype: + logger.info("Adding changetype to bitcoin.conf") + with open(btc_conf_path, "a") as fp: + fp.write("changetype=bech32\n") args = [ daemon_bin, @@ -474,6 +502,7 @@ def runClient( "stdout_to_file": True, "stdout_filename": "dcrd_stdout.log", "use_shell": use_shell, + "coin_name": "decred", } daemons.append( startDaemon( @@ -502,6 +531,7 @@ def runClient( "stdout_to_file": True, "stdout_filename": "dcrwallet_stdout.log", "use_shell": use_shell, + "coin_name": "decred", } daemons.append( startDaemon( @@ -524,8 +554,15 @@ def runClient( extra_opts = getCoreBinArgs( coin_id, v, use_tor_proxy=swap_client.use_tor_proxy ) + extra_config = {"coin_name": c} daemons.append( - startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts) + startDaemon( + v["datadir"], + v["bindir"], + filename, + opts=extra_opts, + extra_config=extra_config, + ) ) pid = daemons[-1].handle.pid pids.append((c, pid)) diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index 0768fd6..5d9fb44 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -561,7 +561,7 @@ class BTCInterface(Secp256k1Interface): override_feerate = chain_client_settings.get("override_feerate", None) if override_feerate: self._log.debug( - "Fee rate override used for %s: %f", self.coin_name(), override_feerate + f"Fee rate override used for {self.coin_name()}: {override_feerate}" ) return override_feerate, "override_feerate" @@ -1318,22 +1318,37 @@ class BTCInterface(Secp256k1Interface): rv = self.rpc_wallet("fundrawtransaction", [tx.hex(), options]) return bytes.fromhex(rv["hex"]) - def lockNonSegwitPrevouts(self) -> None: - # For tests - unspent = self.rpc_wallet("listunspent") - - to_lock = [] - for u in unspent: + def getNonSegwitOutputs(self): + unspents = self.rpc_wallet("listunspent", [0, 99999999]) + nonsegwit_unspents = [] + for u in unspents: 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"]}) + nonsegwit_unspents.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"]}) + nonsegwit_unspents.append( + { + "txid": u["txid"], + "vout": u["vout"], + "amount": u["amount"], + } + ) + return nonsegwit_unspents + + def lockNonSegwitPrevouts(self) -> None: + # For tests + to_lock = self.getNonSegwitOutputs() if len(to_lock) > 0: self._log.debug(f"Locking {len(to_lock)} non segwit prevouts") @@ -1661,7 +1676,7 @@ class BTCInterface(Secp256k1Interface): "listunspent", [ 0, - 9999999, + 99999999, [ dest_address, ], @@ -2392,6 +2407,59 @@ class BTCInterface(Secp256k1Interface): def isTxNonFinalError(self, err_str: str) -> bool: return "non-BIP68-final" in err_str or "non-final" in err_str + def combine_non_segwit_prevouts(self): + self._log.info("Combining non-segwit prevouts") + if self._use_segwit is False: + raise RuntimeError("Not configured to use segwit outputs.") + prevouts_to_spend = self.getNonSegwitOutputs() + if len(prevouts_to_spend) < 1: + raise RuntimeError("No non-segwit outputs found.") + + total_amount: int = 0 + for n, prevout in enumerate(prevouts_to_spend): + total_amount += self.make_int(prevout["amount"]) + addr_to: str = self.getNewAddress( + self._use_segwit, "combine_non_segwit_prevouts" + ) + + txn = self.rpc( + "createrawtransaction", + [prevouts_to_spend, {addr_to: self.format_amount(total_amount)}], + ) + fee_rate, rate_src = self.get_fee_rate(self._conf_target) + fee_rate_str: str = self.format_amount(fee_rate, True, 1) + self._log.debug( + f"Using fee rate: {fee_rate_str}, src: {rate_src}, confirms target: {self._conf_target}" + ) + options = { + "add_inputs": False, + "subtractFeeFromOutputs": [ + 0, + ], + "feeRate": fee_rate_str, + } + tx_fee_set = self.rpc_wallet("fundrawtransaction", [txn, options])["hex"] + tx_signed = self.rpc_wallet("signrawtransactionwithwallet", [tx_fee_set])["hex"] + tx = self.rpc( + "decoderawtransaction", + [ + tx_signed, + ], + ) + self._log.info( + "Submitting tx to combine non-segwit prevouts: {}".format( + self._log.id(bytes.fromhex(tx["txid"])) + ) + ) + self.rpc( + "sendrawtransaction", + [ + tx_signed, + ], + ) + + return tx["txid"] + def testBTCInterface(): print("TODO: testBTCInterface") diff --git a/basicswap/interface/part.py b/basicswap/interface/part.py index 1bae935..9e33563 100644 --- a/basicswap/interface/part.py +++ b/basicswap/interface/part.py @@ -187,6 +187,9 @@ class PARTInterface(BTCInterface): ) + self.make_int(u["amount"], r=1) return unspent_addr + def combine_non_segwit_prevouts(self): + raise RuntimeError("No non-segwit outputs found.") + class PARTInterfaceBlind(PARTInterface): diff --git a/basicswap/templates/debug.html b/basicswap/templates/debug.html index fb3b9f8..0677713 100644 --- a/basicswap/templates/debug.html +++ b/basicswap/templates/debug.html @@ -1,5 +1,5 @@ {% include 'header.html' %} -{% from 'style.html' import breadcrumb_line_svg, start_process_svg %} +{% from 'style.html' import breadcrumb_line_svg, start_process_svg %}