Pass ZCIncrementalMerkleTree to wallet to prevent race conditions

This commit is contained in:
Jack Grigg
2016-08-31 02:00:11 +12:00
parent 769e031c1a
commit de42390f90
6 changed files with 29 additions and 42 deletions

View File

@@ -2398,13 +2398,16 @@ bool static DisconnectTip(CValidationState &state) {
mempool.check(pcoinsTip); mempool.check(pcoinsTip);
// Update chainActive and related variables. // Update chainActive and related variables.
UpdateTip(pindexDelete->pprev); UpdateTip(pindexDelete->pprev);
// Get the current commitment tree
ZCIncrementalMerkleTree newTree;
assert(pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), newTree));
// Let wallets know transactions went from 1-confirmed to // Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted: // 0-confirmed or conflicted:
BOOST_FOREACH(const CTransaction &tx, block.vtx) { BOOST_FOREACH(const CTransaction &tx, block.vtx) {
SyncWithWallets(tx, NULL); SyncWithWallets(tx, NULL);
} }
// Update cached incremental witnesses // Update cached incremental witnesses
GetMainSignals().ChainTip(pindexDelete, &block, false); GetMainSignals().ChainTip(pindexDelete, &block, newTree, false);
return true; return true;
} }
@@ -2429,6 +2432,9 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
return AbortNode(state, "Failed to read block"); return AbortNode(state, "Failed to read block");
pblock = █ pblock = █
} }
// Get the current commitment tree
ZCIncrementalMerkleTree oldTree;
assert(pcoinsTip->GetAnchorAt(pcoinsTip->GetBestAnchor(), oldTree));
// Apply the block atomically to the chain state. // Apply the block atomically to the chain state.
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3; int64_t nTime3;
@@ -2471,7 +2477,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
SyncWithWallets(tx, pblock); SyncWithWallets(tx, pblock);
} }
// Update cached incremental witnesses // Update cached incremental witnesses
GetMainSignals().ChainTip(pindexNew, pblock, true); GetMainSignals().ChainTip(pindexNew, pblock, oldTree, true);
int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1;
LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); LogPrint("bench", " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001);

View File

@@ -16,7 +16,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2)); g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3)); g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3, _4));
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
@@ -27,7 +27,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2)); g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1)); g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1));
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3)); g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3, _4));
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1)); g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));

View File

@@ -8,6 +8,8 @@
#include <boost/signals2/signal.hpp> #include <boost/signals2/signal.hpp>
#include "zcash/IncrementalMerkleTree.hpp"
class CBlock; class CBlock;
class CBlockIndex; class CBlockIndex;
struct CBlockLocator; struct CBlockLocator;
@@ -31,7 +33,7 @@ class CValidationInterface {
protected: protected:
virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {} virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}
virtual void EraseFromWallet(const uint256 &hash) {} virtual void EraseFromWallet(const uint256 &hash) {}
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, bool added) {} virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, ZCIncrementalMerkleTree tree, bool added) {}
virtual void SetBestChain(const CBlockLocator &locator) {} virtual void SetBestChain(const CBlockLocator &locator) {}
virtual void UpdatedTransaction(const uint256 &hash) {} virtual void UpdatedTransaction(const uint256 &hash) {}
virtual void Inventory(const uint256 &hash) {} virtual void Inventory(const uint256 &hash) {}
@@ -50,7 +52,7 @@ struct CMainSignals {
/** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */
boost::signals2::signal<void (const uint256 &)> UpdatedTransaction; boost::signals2::signal<void (const uint256 &)> UpdatedTransaction;
/** Notifies listeners of a change to the tip of the active block chain. */ /** Notifies listeners of a change to the tip of the active block chain. */
boost::signals2::signal<void (const CBlockIndex *, const CBlock *, bool)> ChainTip; boost::signals2::signal<void (const CBlockIndex *, const CBlock *, ZCIncrementalMerkleTree, bool)> ChainTip;
/** Notifies listeners of a new active block chain. */ /** Notifies listeners of a new active block chain. */
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain; boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
/** Notifies listeners about an inventory item being seen on the network. */ /** Notifies listeners about an inventory item being seen on the network. */

View File

@@ -29,8 +29,8 @@ public:
void IncrementNoteWitnesses(const CBlockIndex* pindex, void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock, const CBlock* pblock,
const CCoinsViewCache* pcoins) { ZCIncrementalMerkleTree tree) {
CWallet::IncrementNoteWitnesses(pindex, pblock, pcoins); CWallet::IncrementNoteWitnesses(pindex, pblock, tree);
} }
void DecrementNoteWitnesses() { void DecrementNoteWitnesses() {
CWallet::DecrementNoteWitnesses(); CWallet::DecrementNoteWitnesses();
@@ -328,10 +328,8 @@ TEST(wallet_tests, cached_witnesses_empty_chain) {
CBlock block; CBlock block;
block.vtx.push_back(wtx); block.vtx.push_back(wtx);
MockCCoinsViewCache coins; ZCIncrementalMerkleTree tree;
// Empty chain, so we shouldn't try to fetch an anchor wallet.IncrementNoteWitnesses(NULL, &block, tree);
EXPECT_CALL(coins, GetAnchorAt(_, _)).Times(0);
wallet.IncrementNoteWitnesses(NULL, &block, &coins);
witnesses.clear(); witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor); wallet.GetNoteWitnesses(notes, witnesses, anchor);
EXPECT_TRUE((bool) witnesses[0]); EXPECT_TRUE((bool) witnesses[0]);
@@ -344,9 +342,9 @@ TEST(wallet_tests, cached_witnesses_empty_chain) {
TEST(wallet_tests, cached_witnesses_chain_tip) { TEST(wallet_tests, cached_witnesses_chain_tip) {
TestWallet wallet; TestWallet wallet;
MockCCoinsViewCache coins;
uint256 anchor1; uint256 anchor1;
CBlock block1; CBlock block1;
ZCIncrementalMerkleTree tree;
auto sk = libzcash::SpendingKey::random(); auto sk = libzcash::SpendingKey::random();
wallet.AddSpendingKey(sk); wallet.AddSpendingKey(sk);
@@ -369,9 +367,7 @@ TEST(wallet_tests, cached_witnesses_chain_tip) {
// First block (case tested in _empty_chain) // First block (case tested in _empty_chain)
block1.vtx.push_back(wtx); block1.vtx.push_back(wtx);
EXPECT_CALL(coins, GetAnchorAt(_, _)) wallet.IncrementNoteWitnesses(NULL, &block1, tree);
.Times(0);
wallet.IncrementNoteWitnesses(NULL, &block1, &coins);
// Called to fetch anchor // Called to fetch anchor
wallet.GetNoteWitnesses(notes, witnesses, anchor1); wallet.GetNoteWitnesses(notes, witnesses, anchor1);
} }
@@ -400,10 +396,8 @@ TEST(wallet_tests, cached_witnesses_chain_tip) {
CBlock block2; CBlock block2;
block2.hashPrevBlock = block1.GetHash(); block2.hashPrevBlock = block1.GetHash();
block2.vtx.push_back(wtx); block2.vtx.push_back(wtx);
EXPECT_CALL(coins, GetAnchorAt(anchor1, _)) ZCIncrementalMerkleTree tree2 {tree};
.Times(2) wallet.IncrementNoteWitnesses(NULL, &block2, tree2);
.WillRepeatedly(Return(true));
wallet.IncrementNoteWitnesses(NULL, &block2, &coins);
witnesses.clear(); witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor2); wallet.GetNoteWitnesses(notes, witnesses, anchor2);
EXPECT_TRUE((bool) witnesses[0]); EXPECT_TRUE((bool) witnesses[0]);
@@ -419,7 +413,7 @@ TEST(wallet_tests, cached_witnesses_chain_tip) {
// Re-incrementing with the same block should give the same result // Re-incrementing with the same block should give the same result
uint256 anchor4; uint256 anchor4;
wallet.IncrementNoteWitnesses(NULL, &block2, &coins); wallet.IncrementNoteWitnesses(NULL, &block2, tree);
witnesses.clear(); witnesses.clear();
wallet.GetNoteWitnesses(notes, witnesses, anchor4); wallet.GetNoteWitnesses(notes, witnesses, anchor4);
EXPECT_TRUE((bool) witnesses[0]); EXPECT_TRUE((bool) witnesses[0]);

View File

@@ -336,10 +336,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
return false; return false;
} }
void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock, bool added) void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock,
ZCIncrementalMerkleTree tree, bool added)
{ {
if (added) { if (added) {
IncrementNoteWitnesses(pindex, pblock, pcoinsTip); IncrementNoteWitnesses(pindex, pblock, tree);
} else { } else {
DecrementNoteWitnesses(); DecrementNoteWitnesses();
} }
@@ -594,7 +595,7 @@ void CWallet::AddToSpends(const uint256& wtxid)
void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex, void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblockIn, const CBlock* pblockIn,
const CCoinsViewCache* pcoins) ZCIncrementalMerkleTree tree)
{ {
{ {
LOCK(cs_wallet); LOCK(cs_wallet);
@@ -618,23 +619,7 @@ void CWallet::IncrementNoteWitnesses(const CBlockIndex* pindex,
pblock = &block; pblock = &block;
} }
ZCIncrementalMerkleTree tree;
bool treeInitialised = false;
for (const CTransaction& tx : pblock->vtx) { for (const CTransaction& tx : pblock->vtx) {
if (!treeInitialised && tx.vjoinsplit.size() > 0) {
LOCK(cs_main);
// vAnchorCache will only be empty at the beginning
if (vAnchorCache.size() && !pcoins->GetAnchorAt(vAnchorCache.front(), tree)) {
// This should not happen, because IncrementNoteWitnesses()
// is only called when the chain tip updates, and the
// anchors for the JoinSplits in that block should still be
// cached.
// TODO: Calculate the anchor from scratch?
throw std::runtime_error("CWallet::IncrementNoteWitnesses(): anchor not cached");
}
treeInitialised = true;
}
auto hash = tx.GetTxid(); auto hash = tx.GetTxid();
bool txIsOurs = mapWallet.count(hash); bool txIsOurs = mapWallet.count(hash);
for (size_t i = 0; i < tx.vjoinsplit.size(); i++) { for (size_t i = 0; i < tx.vjoinsplit.size(); i++) {

View File

@@ -588,7 +588,7 @@ public:
protected: protected:
void IncrementNoteWitnesses(const CBlockIndex* pindex, void IncrementNoteWitnesses(const CBlockIndex* pindex,
const CBlock* pblock, const CBlock* pblock,
const CCoinsViewCache* pcoins); ZCIncrementalMerkleTree tree);
void DecrementNoteWitnesses(); void DecrementNoteWitnesses();
private: private:
@@ -810,7 +810,7 @@ public:
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const; CAmount GetChange(const CTransaction& tx) const;
void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, bool added); void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, ZCIncrementalMerkleTree tree, bool added);
void SetBestChain(const CBlockLocator& loc); void SetBestChain(const CBlockLocator& loc);
DBErrors LoadWallet(bool& fFirstRunRet); DBErrors LoadWallet(bool& fFirstRunRet);