Skip to content

Commit fbf485c

Browse files
committed
Allow tr() import only when Taproot is active
To avoid issues around fund loss, only allow descriptor wallets to import tr() descriptors after taproot has activated.
1 parent 346e52a commit fbf485c

File tree

4 files changed

+45
-2
lines changed

4 files changed

+45
-2
lines changed

src/interfaces/chain.h

+3
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ class Chain
277277
//! to be prepared to handle this by ignoring notifications about unknown
278278
//! removed transactions and already added new transactions.
279279
virtual void requestMempoolTransactions(Notifications& notifications) = 0;
280+
281+
//! Check if Taproot has activated
282+
virtual bool isTaprootActive() const = 0;
280283
};
281284

282285
//! Interface to let node manage chain clients (wallets, or maybe tools for

src/node/interfaces.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,12 @@ class ChainImpl : public Chain
709709
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
710710
}
711711
}
712+
bool isTaprootActive() const override
713+
{
714+
LOCK(::cs_main);
715+
const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
716+
return VersionBitsState(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE;
717+
}
712718
NodeContext& m_node;
713719
};
714720
} // namespace

src/wallet/rpcdump.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
15301530
}
15311531
}
15321532

1533+
// Taproot descriptors cannot be imported if Taproot is not yet active.
1534+
// Check if this is a Taproot descriptor
1535+
CTxDestination dest;
1536+
ExtractDestination(scripts[0], dest);
1537+
if (std::holds_alternative<WitnessV1Taproot>(dest)) {
1538+
// Check if Taproot is active
1539+
if (!wallet.chain().isTaprootActive()) {
1540+
// Taproot is not active, raise an error
1541+
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
1542+
}
1543+
}
1544+
15331545
// If private keys are enabled, check some things.
15341546
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
15351547
if (keys.keys.empty()) {

test/functional/wallet_taproot.py

+24-2
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ class WalletTaprootTest(BitcoinTestFramework):
172172
"""Test generation and spending of P2TR address outputs."""
173173

174174
def set_test_params(self):
175-
self.num_nodes = 2
175+
self.num_nodes = 3
176176
self.setup_clean_chain = True
177-
self.extra_args = [['-keypool=100'], ['-keypool=100']]
177+
self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
178178
self.supports_cli = False
179179

180180
def skip_test_if_missing_module(self):
@@ -230,12 +230,34 @@ def do_test_addr(self, comment, pattern, privmap, treefn, keys):
230230
addr_r = self.make_addr(treefn, keys, i)
231231
assert_equal(addr_g, addr_r)
232232

233+
# tr descriptors cannot be imported when Taproot is not active
234+
result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
235+
assert(result[0]["success"])
236+
result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
237+
assert(not result[0]["success"])
238+
assert_equal(result[0]["error"]["code"], -4)
239+
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
240+
result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
241+
assert(result[0]["success"])
242+
result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
243+
assert(not result[0]["success"])
244+
assert_equal(result[0]["error"]["code"], -4)
245+
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
246+
233247
def do_test(self, comment, pattern, privmap, treefn, nkeys):
234248
keys = self.rand_keys(nkeys)
235249
self.do_test_addr(comment, pattern, privmap, treefn, keys)
236250

237251
def run_test(self):
238252
self.log.info("Creating wallets...")
253+
self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True)
254+
self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled")
255+
self.nodes[2].createwallet(wallet_name="privs_tr_disabled", descriptors=True, blank=True)
256+
self.privs_tr_disabled=self.nodes[2].get_wallet_rpc("privs_tr_disabled")
257+
self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True)
258+
self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled")
259+
self.nodes[2].createwallet(wallet_name="pubs_tr_disabled", descriptors=True, blank=True, disable_private_keys=True)
260+
self.pubs_tr_disabled=self.nodes[2].get_wallet_rpc("pubs_tr_disabled")
239261
self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
240262
self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")
241263

0 commit comments

Comments
 (0)