From 4871bae163dd111a941a0d215c93894b653cfc2e Mon Sep 17 00:00:00 2001 From: David Burkett Date: Tue, 31 May 2022 15:07:33 -0400 Subject: [PATCH] Adding functional test for MWEB functionality after upgrading non-HD wallet --- src/wallet/reserve.cpp | 4 +- src/wallet/scriptpubkeyman.cpp | 3 +- test/functional/mweb_wallet_upgrade.py | 97 ++++++++++++++++++++++ test/functional/test_framework/ltc_util.py | 45 +++++++++- test/functional/test_runner.py | 1 + 5 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 test/functional/mweb_wallet_upgrade.py diff --git a/src/wallet/reserve.cpp b/src/wallet/reserve.cpp index 030b3a39a69c3..231031f81f251 100644 --- a/src/wallet/reserve.cpp +++ b/src/wallet/reserve.cpp @@ -13,9 +13,11 @@ bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool inter m_spk_man->TopUp(); CKeyPool keypool; - if (!m_spk_man->GetReservedDestination(type, internal, address, nIndex, keypool)) { + int64_t reserved_index; + if (!m_spk_man->GetReservedDestination(type, internal, address, reserved_index, keypool)) { return false; } + nIndex = reserved_index; fInternal = keypool.fInternal; } dest = address; diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 865264808aa36..b66e7086f170f 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -1441,6 +1441,7 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key bool fReturningInternal = (purpose == KeyPurpose::INTERNAL); fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); bool fMWEB = (purpose == KeyPurpose::MWEB) && IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT); + bool use_pre_split = !fMWEB && !set_pre_split_keypool.empty(); auto fn_get_keypool = [this](const bool internal, const bool mweb) -> std::set& { if (mweb) { @@ -1477,7 +1478,7 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } // If the key was pre-split keypool, we don't care about what type it is - if (set_pre_split_keypool.empty() && keypool.fInternal != fReturningInternal) { + if (!use_pre_split && keypool.fInternal != fReturningInternal) { throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } if (!keypool.vchPubKey.IsValid()) { diff --git a/test/functional/mweb_wallet_upgrade.py b/test/functional/mweb_wallet_upgrade.py new file mode 100644 index 0000000000000..110b44f9ac1ce --- /dev/null +++ b/test/functional/mweb_wallet_upgrade.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# Copyright (c) 2021 The Litecoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Tests that non-HD wallets that are upgraded are able to receive via MWEB""" + +import os +import shutil + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.ltc_util import create_non_hd_wallet, setup_mweb_chain +from test_framework.util import assert_equal + +class MWEBWalletUpgradeTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [['-whitelist=noban@127.0.0.1'],[]] # immediate tx relay + + def skip_test_if_missing_module(self): + self.skip_if_no_previous_releases() + self.skip_if_no_wallet() + + def run_test(self): + node0 = self.nodes[0] + node1 = self.nodes[1] + + # + # Mine until MWEB is activated + # + self.log.info("Setting up MWEB chain") + setup_mweb_chain(node0) + self.sync_all() + + # + # Create a non-HD wallet using an older litecoin core version + # + self.log.info("Creating non-hd wallet") + nonhd_wallet_dat = create_non_hd_wallet(self.chain, self.options) + + # + # Replace node1's wallet with the non-HD wallet.dat + # + #self.log.info("Replacing wallet with non-hd wallet.dat") + node1.get_wallet_rpc(self.default_wallet_name).unloadwallet() + upgrade_wallet_dir = os.path.join(node1.datadir, "regtest", "wallets", self.default_wallet_name) + shutil.rmtree(upgrade_wallet_dir) + os.mkdir(upgrade_wallet_dir) + shutil.copy(nonhd_wallet_dat, upgrade_wallet_dir) + node1.loadwallet(self.default_wallet_name) + + # + # Upgrade node1's non-HD wallet to the latest version + # + self.log.info("Upgrading wallet") + node1.upgradewallet() + + # + # Send to MWEB address of upgraded wallet (node1) + # + self.log.info("Send to upgraded wallet's mweb address") + mweb_addr = node1.getnewaddress(address_type='mweb') + tx1_id = node0.sendtoaddress(mweb_addr, 25) + self.sync_mempools() + + # + # Verify transaction is received by upgraded wallet (node1) + # + self.log.info("Verify upgraded wallet lists the transaction") + tx1 = node1.gettransaction(txid=tx1_id) + assert_equal(tx1['confirmations'], 0) + assert_equal(tx1['amount'], 25) + assert_equal(tx1['details'][0]['address'], mweb_addr) + + node0.generate(1) + self.sync_all() + + # + # Verify that MWEB coins can be spent by upgraded wallet (node1) + # + self.log.info("Spending MWEB coins") + mining_mweb_addr = node0.getnewaddress(address_type='mweb') + tx2_id = node1.sendtoaddress(mining_mweb_addr, 10) + self.sync_mempools() + + # + # Mine 1 block and verify transaction confirms + # + self.log.info("Mining block to verify it confirms") + node0.generate(1) + tx2 = node0.gettransaction(txid=tx2_id) + assert_equal(tx2['confirmations'], 1) + assert_equal(tx2['amount'], 10) + assert_equal(tx2['details'][0]['address'], mining_mweb_addr) + +if __name__ == '__main__': + MWEBWalletUpgradeTest().main() diff --git a/test/functional/test_framework/ltc_util.py b/test/functional/test_framework/ltc_util.py index ebb00bd9d28de..9a01b7d9cd11b 100644 --- a/test/functional/test_framework/ltc_util.py +++ b/test/functional/test_framework/ltc_util.py @@ -4,9 +4,12 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Random assortment of utility functions""" +import os + from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxOut, MWEBHeader -from test_framework.util import satoshi_round +from test_framework.util import get_datadir_path, initialize_datadir, satoshi_round from test_framework.script_util import DUMMY_P2WPKH_SCRIPT, hogaddr_script +from test_framework.test_node import TestNode """Create a txout with a given amount and scriptPubKey @@ -91,5 +94,43 @@ def create_hogex(node, mweb_hash): tx.vout = [CTxOut(int(hog_addr['value'] * COIN), hogaddr_script(mweb_hash))] tx.hogex = True tx.rehash() + return tx + +""" Create a non-HD wallet from a temporary v15.1.0 node. - return tx \ No newline at end of file +Returns the path of the wallet.dat. +""" +def create_non_hd_wallet(chain, options): + version = 150100 + bin_dir = os.path.join(options.previous_releases_path, 'v0.15.1', 'bin') + initialize_datadir(options.tmpdir, 10, chain) + data_dir = get_datadir_path(options.tmpdir, 10) + + # adjust conf for pre 17 + conf_file = os.path.join(data_dir, 'litecoin.conf') + with open(conf_file, 'r', encoding='utf8') as conf: + conf_data = conf.read() + with open(conf_file, 'w', encoding='utf8') as conf: + conf.write(conf_data.replace('[regtest]', '')) + + v15_node = TestNode( + i=10, + datadir=data_dir, + chain=chain, + rpchost=None, + timewait=60, + timeout_factor=1.0, + bitcoind=os.path.join(bin_dir, 'litecoind'), + bitcoin_cli=os.path.join(bin_dir, 'litecoin-cli'), + version=version, + coverage_dir=None, + cwd=options.tmpdir, + extra_args=["-usehd=0"], + ) + v15_node.start() + v15_node.wait_for_cookie_credentials() # ensure cookie file is available to avoid race condition + v15_node.wait_for_rpc_connection() + v15_node.stop_node(wait=0) + v15_node.wait_until_stopped() + + return os.path.join(v15_node.datadir, chain, "wallet.dat") \ No newline at end of file diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index ba39011eddde2..c43ebb33b988e 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -252,6 +252,7 @@ 'mweb_node_compatibility.py', 'mweb_wallet_address.py', 'mweb_wallet_basic.py', + 'mweb_wallet_upgrade.py', 'wallet_listwallettransactions.py', 'rpc_uptime.py', 'wallet_resendwallettransactions.py',