Skip to content

Commit

Permalink
Allow tr() import only when Taproot is active
Browse files Browse the repository at this point in the history
To avoid issues around fund loss, only allow descriptor wallets
to import tr() descriptors after taproot has activated.
  • Loading branch information
achow101 committed Jun 10, 2021
1 parent 346e52a commit fbf485c
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/interfaces/chain.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,9 @@ class Chain
//! to be prepared to handle this by ignoring notifications about unknown
//! removed transactions and already added new transactions.
virtual void requestMempoolTransactions(Notifications& notifications) = 0;

//! Check if Taproot has activated
virtual bool isTaprootActive() const = 0;
};

//! Interface to let node manage chain clients (wallets, or maybe tools for
Expand Down
6 changes: 6 additions & 0 deletions src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,12 @@ class ChainImpl : public Chain
notifications.transactionAddedToMempool(entry.GetSharedTx(), 0 /* mempool_sequence */);
}
}
bool isTaprootActive() const override
{
LOCK(::cs_main);
const CBlockIndex* tip = Assert(m_node.chainman)->ActiveChain().Tip();
return VersionBitsState(tip, Params().GetConsensus(), Consensus::DEPLOYMENT_TAPROOT, versionbitscache) == ThresholdState::ACTIVE;
}
NodeContext& m_node;
};
} // namespace
Expand Down
12 changes: 12 additions & 0 deletions src/wallet/rpcdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,18 @@ static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, c
}
}

// Taproot descriptors cannot be imported if Taproot is not yet active.
// Check if this is a Taproot descriptor
CTxDestination dest;
ExtractDestination(scripts[0], dest);
if (std::holds_alternative<WitnessV1Taproot>(dest)) {
// Check if Taproot is active
if (!wallet.chain().isTaprootActive()) {
// Taproot is not active, raise an error
throw JSONRPCError(RPC_WALLET_ERROR, "Cannot import tr() descriptor when Taproot is not active");
}
}

// If private keys are enabled, check some things.
if (!wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
if (keys.keys.empty()) {
Expand Down
26 changes: 24 additions & 2 deletions test/functional/wallet_taproot.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,9 @@ class WalletTaprootTest(BitcoinTestFramework):
"""Test generation and spending of P2TR address outputs."""

def set_test_params(self):
self.num_nodes = 2
self.num_nodes = 3
self.setup_clean_chain = True
self.extra_args = [['-keypool=100'], ['-keypool=100']]
self.extra_args = [['-keypool=100'], ['-keypool=100'], ["-vbparams=taproot:1:1"]]
self.supports_cli = False

def skip_test_if_missing_module(self):
Expand Down Expand Up @@ -230,12 +230,34 @@ def do_test_addr(self, comment, pattern, privmap, treefn, keys):
addr_r = self.make_addr(treefn, keys, i)
assert_equal(addr_g, addr_r)

# tr descriptors cannot be imported when Taproot is not active
result = self.privs_tr_enabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
assert(result[0]["success"])
result = self.privs_tr_disabled.importdescriptors([{"desc": desc, "timestamp": "now"}])
assert(not result[0]["success"])
assert_equal(result[0]["error"]["code"], -4)
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")
result = self.pubs_tr_enabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
assert(result[0]["success"])
result = self.pubs_tr_disabled.importdescriptors([{"desc": desc_pub, "timestamp": "now"}])
assert(not result[0]["success"])
assert_equal(result[0]["error"]["code"], -4)
assert_equal(result[0]["error"]["message"], "Cannot import tr() descriptor when Taproot is not active")

def do_test(self, comment, pattern, privmap, treefn, nkeys):
keys = self.rand_keys(nkeys)
self.do_test_addr(comment, pattern, privmap, treefn, keys)

def run_test(self):
self.log.info("Creating wallets...")
self.nodes[0].createwallet(wallet_name="privs_tr_enabled", descriptors=True, blank=True)
self.privs_tr_enabled = self.nodes[0].get_wallet_rpc("privs_tr_enabled")
self.nodes[2].createwallet(wallet_name="privs_tr_disabled", descriptors=True, blank=True)
self.privs_tr_disabled=self.nodes[2].get_wallet_rpc("privs_tr_disabled")
self.nodes[0].createwallet(wallet_name="pubs_tr_enabled", descriptors=True, blank=True, disable_private_keys=True)
self.pubs_tr_enabled = self.nodes[0].get_wallet_rpc("pubs_tr_enabled")
self.nodes[2].createwallet(wallet_name="pubs_tr_disabled", descriptors=True, blank=True, disable_private_keys=True)
self.pubs_tr_disabled=self.nodes[2].get_wallet_rpc("pubs_tr_disabled")
self.nodes[0].createwallet(wallet_name="addr_gen", descriptors=True, disable_private_keys=True, blank=True)
self.addr_gen = self.nodes[0].get_wallet_rpc("addr_gen")

Expand Down

0 comments on commit fbf485c

Please sign in to comment.