Browse Source

Port PR93 from @denioD

pull/95/head
Duke Leto 4 years ago
parent
commit
a2b3316664
  1. 3
      qa/rpc-tests/mempool_resurrect_test.py
  2. 8
      qa/rpc-tests/test_framework/util.py
  3. 1
      qa/rpc-tests/wallet.py
  4. 2
      qa/rpc-tests/wallet_1941.py
  5. 1
      qa/rpc-tests/wallet_anchorfork.py
  6. 1
      qa/rpc-tests/wallet_shieldcoinbase.py
  7. 3
      qa/rpc-tests/zcjoinsplit.py
  8. 16
      qa/rpc-tests/zmq_test.py
  9. 31
      src/init.cpp
  10. 83
      src/main.cpp
  11. 4
      src/main.h
  12. 30
      src/txmempool.cpp
  13. 3
      src/txmempool.h
  14. 83
      src/validationinterface.cpp
  15. 8
      src/validationinterface.h
  16. 4
      src/wallet/wallet.cpp
  17. 5
      src/wallet/wallet.h
  18. 4
      src/zcbenchmarks.cpp

3
qa/rpc-tests/mempool_resurrect_test.py

@ -54,6 +54,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
spends2_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw ] spends2_id = [ self.nodes[0].sendrawtransaction(tx) for tx in spends2_raw ]
blocks.extend(self.nodes[0].generate(1)) blocks.extend(self.nodes[0].generate(1))
self.sync_all()
# mempool should be empty, all txns confirmed # mempool should be empty, all txns confirmed
assert_equal(set(self.nodes[0].getrawmempool()), set()) assert_equal(set(self.nodes[0].getrawmempool()), set())
@ -74,6 +75,8 @@ class MempoolCoinbaseTest(BitcoinTestFramework):
# Generate another block, they should all get mined # Generate another block, they should all get mined
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
# mempool should be empty, all txns confirmed # mempool should be empty, all txns confirmed
assert_equal(set(self.nodes[0].getrawmempool()), set()) assert_equal(set(self.nodes[0].getrawmempool()), set())
for txid in spends1_id+spends2_id: for txid in spends1_id+spends2_id:

8
qa/rpc-tests/test_framework/util.py

@ -60,6 +60,14 @@ def sync_blocks(rpc_connections, wait=1):
break break
time.sleep(wait) time.sleep(wait)
# Now that the block counts are in sync, wait for the internal
# notifications to finish
while True:
notified = [ x.getblockchaininfo()['fullyNotified'] for x in rpc_connections ]
if notified == [ True ] * len(notified):
break
time.sleep(wait)
def sync_mempools(rpc_connections, wait=1): def sync_mempools(rpc_connections, wait=1):
""" """
Wait until everybody has the same transactions in their memory Wait until everybody has the same transactions in their memory

1
qa/rpc-tests/wallet.py

@ -31,6 +31,7 @@ class WalletTest (BitcoinTestFramework):
print "Mining blocks..." print "Mining blocks..."
self.nodes[0].generate(4) self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo() walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40) assert_equal(walletinfo['immature_balance'], 40)

2
qa/rpc-tests/wallet_1941.py

@ -45,6 +45,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework):
self.nodes[0].setmocktime(starttime) self.nodes[0].setmocktime(starttime)
self.nodes[0].generate(101) self.nodes[0].generate(101)
self.sync_all()
mytaddr = self.nodes[0].getnewaddress() # where coins were mined mytaddr = self.nodes[0].getnewaddress() # where coins were mined
myzaddr = self.nodes[0].z_getnewaddress() myzaddr = self.nodes[0].z_getnewaddress()
@ -63,6 +64,7 @@ class Wallet1941RegressionTest (BitcoinTestFramework):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.nodes[0].setmocktime(starttime + 9000) self.nodes[0].setmocktime(starttime + 9000)
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
# Confirm the balance on node 0. # Confirm the balance on node 0.
resp = self.nodes[0].z_getbalance(myzaddr) resp = self.nodes[0].z_getbalance(myzaddr)

1
qa/rpc-tests/wallet_anchorfork.py

@ -27,6 +27,7 @@ class WalletAnchorForkTest (BitcoinTestFramework):
def run_test (self): def run_test (self):
print "Mining blocks..." print "Mining blocks..."
self.nodes[0].generate(4) self.nodes[0].generate(4)
self.sync_all()
walletinfo = self.nodes[0].getwalletinfo() walletinfo = self.nodes[0].getwalletinfo()
assert_equal(walletinfo['immature_balance'], 40) assert_equal(walletinfo['immature_balance'], 40)

1
qa/rpc-tests/wallet_shieldcoinbase.py

@ -44,6 +44,7 @@ class WalletShieldCoinbaseTest (BitcoinTestFramework):
print "Mining blocks..." print "Mining blocks..."
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
do_not_shield_taddr = self.nodes[0].getnewaddress() do_not_shield_taddr = self.nodes[0].getnewaddress()
self.nodes[0].generate(4) self.nodes[0].generate(4)

3
qa/rpc-tests/zcjoinsplit.py

@ -30,6 +30,7 @@ class JoinSplitTest(BitcoinTestFramework):
protect_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"]) protect_tx = self.nodes[0].signrawtransaction(joinsplit_result["rawtxn"])
self.nodes[0].sendrawtransaction(protect_tx["hex"]) self.nodes[0].sendrawtransaction(protect_tx["hex"])
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"]) receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])
assert_equal(receive_result["exists"], True) assert_equal(receive_result["exists"], True)
@ -39,6 +40,7 @@ class JoinSplitTest(BitcoinTestFramework):
addrtest = self.nodes[0].getnewaddress() addrtest = self.nodes[0].getnewaddress()
for xx in range(0,10): for xx in range(0,10):
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
for x in range(0,50): for x in range(0,50):
self.nodes[0].sendtoaddress(addrtest, 0.01); self.nodes[0].sendtoaddress(addrtest, 0.01);
@ -47,6 +49,7 @@ class JoinSplitTest(BitcoinTestFramework):
self.nodes[0].sendrawtransaction(joinsplit_result["rawtxn"]) self.nodes[0].sendrawtransaction(joinsplit_result["rawtxn"])
self.nodes[0].generate(1) self.nodes[0].generate(1)
self.sync_all()
print "Done!" print "Done!"
receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"]) receive_result = self.nodes[0].zcrawreceive(zcsecretkey, joinsplit_result["encryptednote1"])

16
qa/rpc-tests/zmq_test.py

@ -39,21 +39,21 @@ class ZMQTest(BitcoinTestFramework):
print "listen..." print "listen..."
msg = self.zmqSubSocket.recv_multipart() msg = self.zmqSubSocket.recv_multipart()
topic = msg[0] topic = msg[0]
assert_equal(topic, b"hashtx")
body = msg[1] body = msg[1]
nseq = msg[2]
[nseq] # hush pyflakes
msgSequence = struct.unpack('<I', msg[-1])[-1] msgSequence = struct.unpack('<I', msg[-1])[-1]
assert_equal(msgSequence, 0) # must be sequence 0 on hashtx assert_equal(msgSequence, 0) #must be sequence 0 on hashblock
blkhash = bytes_to_hex_str(body)
assert_equal(genhashes[0], blkhash) #blockhash from generate must be equal to the hash received over zmq
msg = self.zmqSubSocket.recv_multipart() msg = self.zmqSubSocket.recv_multipart()
topic = msg[0] topic = msg[0]
assert_equal(topic, b"hashtx")
body = msg[1] body = msg[1]
nseq = msg[2]
[nseq] # hush pyflakes
msgSequence = struct.unpack('<I', msg[-1])[-1] msgSequence = struct.unpack('<I', msg[-1])[-1]
assert_equal(msgSequence, 0) #must be sequence 0 on hashblock assert_equal(msgSequence, 0) # must be sequence 0 on hashtx
blkhash = bytes_to_hex_str(body)
assert_equal(genhashes[0], blkhash) #blockhash from generate must be equal to the hash received over zmq
n = 10 n = 10
genhashes = self.nodes[1].generate(n) genhashes = self.nodes[1].generate(n)

31
src/init.cpp

@ -736,22 +736,6 @@ void ThreadImport(std::vector<boost::filesystem::path> vImportFiles)
} }
} }
void ThreadNotifyRecentlyAdded()
{
while (true) {
// Run the notifier on an integer second in the steady clock.
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto nextFire = std::chrono::duration_cast<std::chrono::seconds>(
now + std::chrono::seconds(1));
std::this_thread::sleep_until(
std::chrono::time_point<std::chrono::steady_clock>(nextFire));
boost::this_thread::interruption_point();
mempool.NotifyRecentlyAdded();
}
}
/** Sanity checks /** Sanity checks
* Ensure that Hush is running in a usable environment with all * Ensure that Hush is running in a usable environment with all
* necessary library support. * necessary library support.
@ -2088,6 +2072,17 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
} }
#endif // ENABLE_MINING #endif // ENABLE_MINING
// Start the thread that notifies listeners of transactions that have been
// recently added to the mempool, or have been added to or removed from the
// chain. We perform this before step 10 (import blocks) so that the
// original value of chainActive.Tip(), which corresponds with the wallet's
// view of the chaintip, is passed to ThreadNotifyWallets before the chain
// tip changes again.
boost::function<void()> threadnotifywallets = boost::bind(&ThreadNotifyWallets, chainActive.Tip());
threadGroup.create_thread(
boost::bind(&TraceThread<boost::function<void()>>, "txnotify", threadnotifywallets)
);
// ********************************************************* Step 9: data directory maintenance // ********************************************************* Step 9: data directory maintenance
// if pruning, unset the service bit and perform the initial blockstore prune // if pruning, unset the service bit and perform the initial blockstore prune
@ -2155,10 +2150,6 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0); LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
#endif #endif
// Start the thread that notifies listeners of transactions that have been
// recently added to the mempool.
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "txnotify", &ThreadNotifyRecentlyAdded));
if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
StartTorControl(threadGroup, scheduler); StartTorControl(threadGroup, scheduler);

83
src/main.cpp

@ -4068,21 +4068,8 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
// Update chainActive and related variables. // Update chainActive and related variables.
UpdateTip(pindexDelete->pprev); UpdateTip(pindexDelete->pprev);
// Get the current commitment tree // Updates to connected wallets are triggered by ThreadNotifyWallets
SproutMerkleTree newSproutTree;
SaplingMerkleTree newSaplingTree;
assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), newSproutTree));
assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), newSaplingTree));
// Let wallets know transactions went from 1-confirmed to
// 0-confirmed or conflicted:
std::vector<uint256> TxToRemove;
for (int i = 0; i < block.vtx.size(); i++)
{
CTransaction &tx = block.vtx[i];
SyncWithWallets(tx, NULL);
}
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexDelete, &block, newSproutTree, newSaplingTree, false);
return true; return true;
} }
@ -4156,6 +4143,11 @@ static int64_t nTimeFlush = 0;
static int64_t nTimeChainState = 0; static int64_t nTimeChainState = 0;
static int64_t nTimePostConnect = 0; static int64_t nTimePostConnect = 0;
// Protected by cs_main
std::map<CBlockIndex*, std::list<CTransaction>> recentlyConflictedTxs;
uint64_t nRecentlyConflictedSequence = 0;
uint64_t nNotifiedSequence = 0;
/** /**
* Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock * Connect a new block to chainActive. pblock is either NULL or a pointer to a CBlock
* corresponding to pindexNew, to bypass loading it again from disk. * corresponding to pindexNew, to bypass loading it again from disk.
@ -4175,14 +4167,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
} }
KOMODO_CONNECTING = (int32_t)pindexNew->GetHeight(); KOMODO_CONNECTING = (int32_t)pindexNew->GetHeight();
//fprintf(stderr,"%s connecting ht.%d maxsize.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pindexNew->GetHeight(),MAX_BLOCK_SIZE(pindexNew->GetHeight()),(int32_t)::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION)); //fprintf(stderr,"%s connecting ht.%d maxsize.%d vs %d\n",ASSETCHAINS_SYMBOL,(int32_t)pindexNew->GetHeight(),MAX_BLOCK_SIZE(pindexNew->GetHeight()),(int32_t)::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
// Get the current commitment tree
SproutMerkleTree oldSproutTree;
SaplingMerkleTree oldSaplingTree;
if ( KOMODO_NSPV_FULLNODE )
{
assert(pcoinsTip->GetSproutAnchorAt(pcoinsTip->GetBestAnchor(SPROUT), oldSproutTree));
assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), oldSaplingTree));
}
// 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;
@ -4221,7 +4206,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); LogPrint("bench", " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001);
// Remove conflicting transactions from the mempool. // Remove conflicting transactions from the mempool.
list<CTransaction> txConflicted; std::list<CTransaction> txConflicted;
mempool.removeForBlock(pblock->vtx, pindexNew->GetHeight(), txConflicted, !IsInitialBlockDownload()); mempool.removeForBlock(pblock->vtx, pindexNew->GetHeight(), txConflicted, !IsInitialBlockDownload());
// Remove transactions that expire at new block height from mempool // Remove transactions that expire at new block height from mempool
@ -4231,18 +4216,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
UpdateTip(pindexNew); UpdateTip(pindexNew);
if ( KOMODO_NSPV_FULLNODE ) if ( KOMODO_NSPV_FULLNODE )
{ {
// Tell wallet about transactions that went from mempool
// to conflicted: // Cache the conflicted transactions for subsequent notification.
BOOST_FOREACH(const CTransaction &tx, txConflicted) { // Updates to connected wallets are triggered by ThreadNotifyWallets
SyncWithWallets(tx, NULL); recentlyConflictedTxs.insert(std::make_pair(pindexNew, txConflicted));
} nRecentlyConflictedSequence += 1;
// ... and about transactions that got confirmed:
BOOST_FOREACH(const CTransaction &tx, pblock->vtx) {
SyncWithWallets(tx, pblock);
}
}
// Update cached incremental witnesses
GetMainSignals().ChainTip(pindexNew, pblock, oldSproutTree, oldSaplingTree, true);
EnforceNodeDeprecation(pindexNew->GetHeight()); EnforceNodeDeprecation(pindexNew->GetHeight());
@ -4278,6 +4256,31 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock *
//fprintf(stderr,"%s: returning true\n", __FUNCTION__); //fprintf(stderr,"%s: returning true\n", __FUNCTION__);
return true; return true;
} }
}
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> DrainRecentlyConflicted()
{
uint64_t recentlyConflictedSequence;
std::map<CBlockIndex*, std::list<CTransaction>> txs;
{
LOCK(cs_main);
recentlyConflictedSequence = nRecentlyConflictedSequence;
txs.swap(recentlyConflictedTxs);
}
return std::make_pair(txs, recentlyConflictedSequence);
}
void SetChainNotifiedSequence(uint64_t recentlyConflictedSequence) {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs_main);
nNotifiedSequence = recentlyConflictedSequence;
}
bool ChainIsFullyNotified() {
assert(Params().NetworkIDString() == "regtest");
LOCK(cs_main);
return nRecentlyConflictedSequence == nNotifiedSequence;
}
/** /**
* Return the tip of the chain with the most work in it, that isn't * Return the tip of the chain with the most work in it, that isn't
@ -5316,10 +5319,10 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C
return(false); return(false);
} }
if (ptx) // if (ptx)
{ // {
SyncWithWallets(*ptx, &block); // SyncWithWallets(*ptx, &block);
} // }
if ( ASSETCHAINS_CC != 0 ) if ( ASSETCHAINS_CC != 0 )
{ {

4
src/main.h

@ -949,4 +949,8 @@ uint64_t CalculateCurrentUsage();
/** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */ /** Return a CMutableTransaction with contextual default values based on set of consensus rules at height */
CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight); CMutableTransaction CreateNewContextualCMutableTransaction(const Consensus::Params& consensusParams, int nHeight);
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> DrainRecentlyConflicted();
void SetChainNotifiedSequence(uint64_t recentlyConflictedSequence);
bool ChainIsFullyNotified();
#endif // BITCOIN_MAIN_H #endif // BITCOIN_MAIN_H

30
src/txmempool.cpp

@ -844,7 +844,7 @@ bool CTxMemPool::nullifierExists(const uint256& nullifier, ShieldedType type) co
} }
} }
void CTxMemPool::NotifyRecentlyAdded() std::pair<std::vector<CTransaction>, uint64_t> CTxMemPool::DrainRecentlyAdded()
{ {
uint64_t recentlyAddedSequence; uint64_t recentlyAddedSequence;
std::vector<CTransaction> txs; std::vector<CTransaction> txs;
@ -857,29 +857,13 @@ void CTxMemPool::NotifyRecentlyAdded()
mapRecentlyAddedTx.clear(); mapRecentlyAddedTx.clear();
} }
// A race condition can occur here between these SyncWithWallets calls, and return std::make_pair(txs, recentlyAddedSequence);
// the ones triggered by block logic (in ConnectTip and DisconnectTip). It }
// is harmless because calling SyncWithWallets(_, NULL) does not alter the
// wallet transaction's block information.
for (auto tx : txs) {
try {
SyncWithWallets(tx, NULL);
} catch (const boost::thread_interrupted&) {
fprintf(stderr,"%s: thread interrupted\n", __FUNCTION__);
throw;
} catch (const std::exception& e) {
PrintExceptionContinue(&e, "CTxMemPool::NotifyRecentlyAdded()");
} catch (...) {
PrintExceptionContinue(NULL, "CTxMemPool::NotifyRecentlyAdded()");
}
}
// Update the notified sequence number. We only need this in regtest mode, void CTxMemPool::SetNotifiedSequence(uint64_t recentlyAddedSequence) {
// and should not lock on cs after calling SyncWithWallets otherwise. assert(Params().NetworkIDString() == "regtest");
if (Params().NetworkIDString() == "regtest") { LOCK(cs);
LOCK(cs); nNotifiedSequence = recentlyAddedSequence;
nNotifiedSequence = recentlyAddedSequence;
}
} }
bool CTxMemPool::IsFullyNotified() { bool CTxMemPool::IsFullyNotified() {

3
src/txmempool.h

@ -240,7 +240,8 @@ public:
bool nullifierExists(const uint256& nullifier, ShieldedType type) const; bool nullifierExists(const uint256& nullifier, ShieldedType type) const;
void NotifyRecentlyAdded(); std::pair<std::vector<CTransaction>, uint64_t> DrainRecentlyAdded();
void SetNotifiedSequence(uint64_t recentlyAddedSequence);
bool IsFullyNotified(); bool IsFullyNotified();
unsigned long size() unsigned long size()

83
src/validationinterface.cpp

@ -5,6 +5,17 @@
#include "validationinterface.h" #include "validationinterface.h"
#include "chainparams.h"
#include "init.h"
#include "main.h"
#include "txmempool.h"
#include "ui_interface.h"
#include <boost/thread.hpp>
#include <chrono>
#include <thread>
static CMainSignals g_signals; static CMainSignals g_signals;
CMainSignals& GetMainSignals() CMainSignals& GetMainSignals()
@ -18,7 +29,7 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn) {
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.RescanWallet.connect(boost::bind(&CValidationInterface::RescanWallet, pwalletIn)); g_signals.RescanWallet.connect(boost::bind(&CValidationInterface::RescanWallet, pwalletIn));
g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3, _4, _5)); g_signals.ChainTip.connect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
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));
@ -29,7 +40,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, _4, _5)); g_signals.ChainTip.disconnect(boost::bind(&CValidationInterface::ChainTip, pwalletIn, _1, _2, _3));
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));
@ -55,10 +66,76 @@ void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) {
g_signals.SyncTransaction(tx, pblock); g_signals.SyncTransaction(tx, pblock);
} }
struct CachedBlockData {
CBlockIndex *pindex;
std::pair<SproutMerkleTree, SaplingMerkleTree> oldTrees;
std::list<CTransaction> txConflicted;
CachedBlockData(
CBlockIndex *pindex,
std::pair<SproutMerkleTree, SaplingMerkleTree> oldTrees,
std::list<CTransaction> txConflicted):
pindex(pindex), oldTrees(oldTrees), txConflicted(txConflicted) {}
};
void ThreadNotifyWallets(CBlockIndex *pindexLastTip)
{
// If pindexLastTip == nullptr, the wallet is at genesis.
// However, the genesis block is not loaded synchronously.
// We need to wait for ThreadImport to finish.
while (pindexLastTip == nullptr) {
{
LOCK(cs_main);
pindexLastTip = chainActive.Genesis();
}
MilliSleep(50);
}
while (true) {
// Run the notifier on an integer second in the steady clock.
auto now = std::chrono::steady_clock::now().time_since_epoch();
auto nextFire = std::chrono::duration_cast<std::chrono::seconds>(
now + std::chrono::seconds(1));
std::this_thread::sleep_until(
std::chrono::time_point<std::chrono::steady_clock>(nextFire));
boost::this_thread::interruption_point();
auto chainParams = Params();
//
// Collect all the state we require
//
// The common ancestor between the last chain tip we notified and the
// current chain tip.
const CBlockIndex *pindexFork;
// The stack of blocks we will notify as having been connected.
// Pushed in reverse, popped in order.
std::vector<CachedBlockData> blockStack;
// Transactions that have been recently conflicted out of the mempool.
std::pair<std::map<CBlockIndex*, std::list<CTransaction>>, uint64_t> recentlyConflicted;
// Transactions that have been recently added to the mempool.
std::pair<std::vector<CTransaction>, uint64_t> recentlyAdded;
{
LOCK(cs_main);
// Figure out the path from the last block we notified to the
// current chain tip.
CBlockIndex *pindex = chainActive.Tip();
pindexFork = chainActive.FindFork(pindexLastTip);
// Fetch recently-conflicted transactions. These will include any
// block that has been connected since the last cycle, but we only
// notify for the conflicts created by the current active chain.
}
}
}
void EraseFromWallets(const uint256 &hash) { void EraseFromWallets(const uint256 &hash) {
g_signals.EraseTransaction(hash); g_signals.EraseTransaction(hash);
} }
void RescanWallets() { void RescanWallets() {
g_signals.RescanWallet(); g_signals.RescanWallet();
} }

8
src/validationinterface.h

@ -26,8 +26,6 @@ void RegisterValidationInterface(CValidationInterface* pwalletIn);
void UnregisterValidationInterface(CValidationInterface* pwalletIn); void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */ /** Unregister all wallets from core */
void UnregisterAllValidationInterfaces(); void UnregisterAllValidationInterfaces();
/** Push an updated transaction to all registered wallets */
void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL);
/** Erase a transaction from all registered wallets */ /** Erase a transaction from all registered wallets */
void EraseFromWallets(const uint256 &hash); void EraseFromWallets(const uint256 &hash);
/** Rescan all registered wallets */ /** Rescan all registered wallets */
@ -39,7 +37,7 @@ 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 RescanWallet() {} virtual void RescanWallet() {}
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added) {} virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> 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) {}
@ -62,7 +60,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 *, SproutMerkleTree, SaplingMerkleTree, bool)> ChainTip; boost::signals2::signal<void (const CBlockIndex *, const CBlock *, boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>>)> 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. */
@ -75,4 +73,6 @@ struct CMainSignals {
CMainSignals& GetMainSignals(); CMainSignals& GetMainSignals();
void ThreadNotifyWallets(CBlockIndex *pindexLastTip);
#endif // BITCOIN_VALIDATIONINTERFACE_H #endif // BITCOIN_VALIDATIONINTERFACE_H

4
src/wallet/wallet.cpp

@ -588,9 +588,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
void CWallet::ChainTip(const CBlockIndex *pindex, void CWallet::ChainTip(const CBlockIndex *pindex,
const CBlock *pblock, const CBlock *pblock,
SproutMerkleTree sproutTree, boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added)
SaplingMerkleTree saplingTree,
bool added)
{ {
if (added) { if (added) {
// Prevent witness cache building && consolidation transactions // Prevent witness cache building && consolidation transactions

5
src/wallet/wallet.h

@ -1253,7 +1253,10 @@ public:
CAmount GetCredit(const CTransaction& tx, int32_t voutNum, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, int32_t voutNum, 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, SproutMerkleTree sproutTree, SaplingMerkleTree saplingTree, bool added); void ChainTip(
const CBlockIndex *pindex,
const CBlock *pblock,
boost::optional<std::pair<SproutMerkleTree, SaplingMerkleTree>> added);
void RunSaplingConsolidation(int blockHeight); void RunSaplingConsolidation(int blockHeight);
void CommitConsolidationTx(const CTransaction& tx); void CommitConsolidationTx(const CTransaction& tx);
/** Saves witness caches and best block locator to disk. */ /** Saves witness caches and best block locator to disk. */

4
src/zcbenchmarks.cpp

@ -309,7 +309,7 @@ double benchmark_increment_note_witnesses(size_t nTxs)
index1.SetHeight(1); index1.SetHeight(1);
// Increment to get transactions witnessed // Increment to get transactions witnessed
wallet.ChainTip(&index1, &block1, sproutTree, saplingTree, true); wallet.ChainTip(&index1, &block1, std::make_pair(sproutTree, saplingTree));
// Second block // Second block
CBlock block2; CBlock block2;
@ -333,7 +333,7 @@ double benchmark_increment_note_witnesses(size_t nTxs)
struct timeval tv_start; struct timeval tv_start;
timer_start(tv_start); timer_start(tv_start);
wallet.ChainTip(&index2, &block2, sproutTree, saplingTree, true); wallet.ChainTip(&index2, &block2, std::make_pair(sproutTree, saplingTree));
return timer_stop(tv_start); return timer_stop(tv_start);
} }

Loading…
Cancel
Save