Skip to content

Commit

Permalink
Merge #1915: Fix block-connection performance regression and mempool …
Browse files Browse the repository at this point in the history
…not copying tx performance improvement

0e59bad Document ConnectBlock connectTrace postconditions (Matt Corallo)
908ffd9 Switch pblock in ProcessNewBlock to a shared_ptr (furszy)
7b38f64 Make the optional pblock in ActivateBestChain a shared_ptr (furszy)
4cf6550 Create a shared_ptr for the block we're connecting in ActivateBCS (Matt Corallo)
140446c Keep blocks as shared_ptrs, instead of copying txn in ConnectTip (Matt Corallo)
9a809a2 Add struct to track block-connect-time-generated info for callbacks. (furszy)
80a94df Migrating to CTransationRef second round: (furszy)
2a89063 Initial mempool migration to using CTransationRef instead of a CTransation copy. (furszy)
bdd5221 Add support for unique_ptr and shared_ptr to memusage (Pieter Wuille)

Pull request description:

  Made my own way up to be able to back port bitcoin#9014. This will mean a performance regression fix over the block-connection process + less memory usage storing `CTransactionRef` instead of a plain `CTransaction` copy in the mempool.

  The first three commits are essentially covering:
   1) mempool: using `CTransationRef` instead of coping each transaction.
   2) validation: ATMP and ATMPW migrated to receive `CTransationRef`.
   3) net_processing: ProcessMessage migrated TX message processing to use `CTransactionRef`.
   4) swiftx: ProcessMessage migrated IX message processing to use `CTransactionRef`.
   5) migrated mapOrphanTransactions to use `CTransactionRef`.

  And from [085dc57](085dc57) starts btc/9014 back porting work.

ACKs for top commit:
  random-zebra:
    tested ACK 0e59bad
  Fuzzbawls:
    ACK 0e59bad

Tree-SHA512: 96d2c77750f5b90a52d688f91b04bf04c198b1c9fa90731d848f48564cd88a8205fa278e78aa519bdc412eae7b189e96c4f52337b8965895b9634a0ad1d3a9b5
  • Loading branch information
furszy committed Oct 17, 2020
2 parents 14f0e97 + 0e59bad commit 8a9b92c
Show file tree
Hide file tree
Showing 19 changed files with 173 additions and 128 deletions.
24 changes: 24 additions & 0 deletions src/memusage.h
Expand Up @@ -80,6 +80,15 @@ struct stl_tree_node
X x;
};

struct stl_shared_counter
{
/* Various platforms use different sized counters here.
* Conservatively assume that they won't be larger than size_t. */
void* class_type;
size_t use_count;
size_t weak_count;
};

template<typename X>
static inline size_t DynamicUsage(const std::vector<X>& v)
{
Expand Down Expand Up @@ -158,6 +167,21 @@ static inline size_t RecursiveDynamicUsage(const std::pair<X, Y>& v)
return RecursiveDynamicUsage(v.first) + RecursiveDynamicUsage(v.second);
}

template<typename X>
static inline size_t DynamicUsage(const std::unique_ptr<X>& p)
{
return p ? MallocUsage(sizeof(X)) : 0;
}

template<typename X>
static inline size_t DynamicUsage(const std::shared_ptr<X>& p)
{
// A shared_ptr can either use a single continuous memory block for both
// the counter and the storage (when using std::make_shared), or separate.
// We can't observe the difference, however, so assume the worst.
return p ? MallocUsage(sizeof(X)) + MallocUsage(sizeof(stl_shared_counter)) : 0;
}

// Boost data structures

template<typename X>
Expand Down
8 changes: 4 additions & 4 deletions src/miner.cpp
Expand Up @@ -470,7 +470,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet,
return pblocktemplate.release();
}

void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
void IncrementExtraNonce(std::shared_ptr<CBlock>& pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
{
// Update nExtraNonce
static uint256 hashPrevBlock;
Expand Down Expand Up @@ -517,7 +517,7 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, CWallet* pwallet)
return CreateNewBlock(scriptPubKey, pwallet, false);
}

bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, Optional<CReserveKey>& reservekey)
bool ProcessBlockFound(const std::shared_ptr<const CBlock>& pblock, CWallet& wallet, Optional<CReserveKey>& reservekey)
{
LogPrintf("%s\n", pblock->ToString());
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0]->vout[0].nValue));
Expand Down Expand Up @@ -628,7 +628,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake)
CreateNewBlock(CScript(), pwallet, true, &availableCoins) :
CreateNewBlockWithKey(*opReservekey, pwallet)));
if (!pblocktemplate.get()) continue;
CBlock* pblock = &pblocktemplate->block;
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(pblocktemplate->block);

// POS - block found: process it
if (fProofOfStake) {
Expand Down Expand Up @@ -713,7 +713,7 @@ void BitcoinMiner(CWallet* pwallet, bool fProofOfStake)
) break;

// Update nTime every few seconds
UpdateTime(pblock, pindexPrev);
UpdateTime(pblock.get(), pindexPrev);
if (Params().GetConsensus().fPowAllowMinDifficultyBlocks) {
// Changing pblock->nTime can change work required on testnet:
hashTarget.SetCompact(pblock->nBits);
Expand Down
2 changes: 1 addition & 1 deletion src/miner.h
Expand Up @@ -26,7 +26,7 @@ struct CBlockTemplate;
/** Generate a new block, without valid proof-of-work */
CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, bool fProofOfStake, std::vector<CStakeableOutput>* availableCoins = nullptr);
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
void IncrementExtraNonce(std::shared_ptr<CBlock>& pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
/** Check mined block */
void UpdateTime(CBlockHeader* block, const CBlockIndex* pindexPrev);

Expand Down
49 changes: 25 additions & 24 deletions src/net_processing.cpp
Expand Up @@ -24,7 +24,7 @@ int64_t nTimeBestReceived = 0; // Used only to inform the wallet of when we las
static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8]

struct COrphanTx {
CTransaction tx;
CTransactionRef tx;
NodeId fromPeer;
};

Expand Down Expand Up @@ -511,9 +511,9 @@ void UnregisterNodeSignals(CNodeSignals& nodeSignals)
// mapOrphanTransactions
//

bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool AddOrphanTx(const CTransactionRef& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
{
uint256 hash = tx.GetHash();
const uint256& hash = tx->GetHash();
if (mapOrphanTransactions.count(hash))
return false;

Expand All @@ -524,15 +524,15 @@ bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(c
// have been mined or received.
// 10,000 orphans, each of which is at most 5,000 bytes big is
// at most 500 megabytes of orphans:
unsigned int sz = GetSerializeSize(tx, SER_NETWORK, CTransaction::CURRENT_VERSION);
unsigned int sz = GetSerializeSize(*tx, SER_NETWORK, CTransaction::CURRENT_VERSION);
if (sz > 5000) {
LogPrint(BCLog::MEMPOOL, "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
return false;
}

mapOrphanTransactions[hash].tx = tx;
mapOrphanTransactions[hash].fromPeer = peer;
for (const CTxIn& txin : tx.vin)
auto ret = mapOrphanTransactions.emplace(hash, COrphanTx{tx, peer});
assert(ret.second);
for (const CTxIn& txin : tx->vin)
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);

LogPrint(BCLog::MEMPOOL, "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(),
Expand All @@ -545,7 +545,7 @@ void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
std::map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
if (it == mapOrphanTransactions.end())
return;
for (const CTxIn& txin : it->second.tx.vin) {
for (const CTxIn& txin : it->second.tx->vin) {
std::map<uint256, std::set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash);
if (itPrev == mapOrphanTransactionsByPrev.end())
continue;
Expand All @@ -563,7 +563,7 @@ void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
while (iter != mapOrphanTransactions.end()) {
std::map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
if (maybeErase->second.fromPeer == peer) {
EraseOrphanTx(maybeErase->second.tx.GetHash());
EraseOrphanTx(maybeErase->second.tx->GetHash());
++nErased;
}
}
Expand Down Expand Up @@ -1401,8 +1401,9 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
else if (strCommand == NetMsgType::TX) {
std::vector<uint256> vWorkQueue;
std::vector<uint256> vEraseQueue;
CTransaction tx;
vRecv >> tx;
CTransactionRef ptx;
vRecv >> ptx;
const CTransaction& tx = *ptx;

CInv inv(MSG_TX, tx.GetHash());
pfrom->AddInventoryKnown(inv);
Expand All @@ -1416,7 +1417,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR

mapAlreadyAskedFor.erase(inv);

if (!tx.HasZerocoinSpendInputs() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs, false, ignoreFees)) {
if (!tx.HasZerocoinSpendInputs() && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, false, ignoreFees)) {
mempool.check(pcoinsTip);
RelayTransaction(tx, connman);
vWorkQueue.push_back(inv.hash);
Expand All @@ -1435,7 +1436,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
mi != itByPrev->second.end();
++mi) {
const uint256 &orphanHash = *mi;
const CTransaction &orphanTx = mapOrphanTransactions[orphanHash].tx;
const auto &orphanTx = mapOrphanTransactions[orphanHash].tx;
NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer;
bool fMissingInputs2 = false;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
Expand All @@ -1448,7 +1449,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
continue;
if(AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2)) {
LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString());
RelayTransaction(orphanTx, connman);
RelayTransaction(*orphanTx, connman);
vWorkQueue.push_back(orphanHash);
vEraseQueue.push_back(orphanHash);
} else if(!fMissingInputs2) {
Expand All @@ -1472,7 +1473,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR

for (uint256& hash : vEraseQueue) EraseOrphanTx(hash);

} else if (tx.HasZerocoinSpendInputs() && AcceptToMemoryPool(mempool, state, tx, true, &fMissingZerocoinInputs, false, false, ignoreFees)) {
} else if (tx.HasZerocoinSpendInputs() && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingZerocoinInputs, false, false, ignoreFees)) {
//Presstab: ZCoin has a bunch of code commented out here. Is this something that should have more going on?
//Also there is nothing that handles fMissingZerocoinInputs. Does there need to be?
RelayTransaction(tx, connman);
Expand All @@ -1481,7 +1482,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
tx.GetHash().ToString(),
mempool.mapTx.size());
} else if (fMissingInputs) {
AddOrphanTx(tx, pfrom->GetId());
AddOrphanTx(ptx, pfrom->GetId());

// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
Expand Down Expand Up @@ -1580,18 +1581,18 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR

else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing
{
CBlock block;
vRecv >> block;
const uint256& hashBlock = block.GetHash();
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
vRecv >> *pblock;
const uint256& hashBlock = pblock->GetHash();
CInv inv(MSG_BLOCK, hashBlock);
LogPrint(BCLog::NET, "received block %s peer=%d\n", inv.hash.ToString(), pfrom->id);

//sometimes we will be sent their most recent block and its not the one we want, in that case tell where we are
if (!mapBlockIndex.count(block.hashPrevBlock)) {
if (!mapBlockIndex.count(pblock->hashPrevBlock)) {
if (find(pfrom->vBlockRequested.begin(), pfrom->vBlockRequested.end(), hashBlock) != pfrom->vBlockRequested.end()) {
//we already asked for this block, so lets work backwards and ask for the previous block
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKS, chainActive.GetLocator(), block.hashPrevBlock));
pfrom->vBlockRequested.push_back(block.hashPrevBlock);
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKS, chainActive.GetLocator(), pblock->hashPrevBlock));
pfrom->vBlockRequested.push_back(pblock->hashPrevBlock);
} else {
//ask to sync to this block
connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKS, chainActive.GetLocator(), hashBlock));
Expand All @@ -1603,7 +1604,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
if (!mapBlockIndex.count(hashBlock)) {
WITH_LOCK(cs_main, MarkBlockAsReceived(hashBlock); );
bool fAccepted = true;
ProcessNewBlock(state, pfrom, &block, nullptr, &fAccepted);
ProcessNewBlock(state, pfrom, pblock, nullptr, &fAccepted);
if (!fAccepted) {
CheckBlockSpam(state, pfrom, hashBlock);
}
Expand All @@ -1621,7 +1622,7 @@ bool static ProcessMessage(CNode* pfrom, std::string strCommand, CDataStream& vR
//disconnect this node if its old protocol version
pfrom->DisconnectOldProtocol(pfrom->nVersion, ActiveProtocol(), strCommand);
} else {
LogPrint(BCLog::NET, "%s : Already processed block %s, skipping ProcessNewBlock()\n", __func__, block.GetHash().GetHex());
LogPrint(BCLog::NET, "%s : Already processed block %s, skipping ProcessNewBlock()\n", __func__, pblock->GetHash().GetHex());
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/rpc/blockchain.cpp
Expand Up @@ -1184,7 +1184,7 @@ UniValue invalidateblock(const JSONRPCRequest& request)
}

if (state.IsValid()) {
ActivateBestChain(state, nullptr, false);
ActivateBestChain(state);
budget.SetBestHeight(WITH_LOCK(cs_main, return chainActive.Height(); ));
}

Expand Down Expand Up @@ -1223,7 +1223,7 @@ UniValue reconsiderblock(const JSONRPCRequest& request)
}

if (state.IsValid()) {
ActivateBestChain(state, nullptr, false);
ActivateBestChain(state);
budget.SetBestHeight(WITH_LOCK(cs_main, return chainActive.Height(); ));
}

Expand Down
7 changes: 4 additions & 3 deletions src/rpc/mining.cpp
Expand Up @@ -169,7 +169,7 @@ UniValue generate(const JSONRPCRequest& request)
CreateNewBlock(CScript(), pwalletMain, true, &availableCoins) :
CreateNewBlockWithKey(reservekey, pwalletMain));
if (!pblocktemplate.get()) break;
CBlock *pblock = &pblocktemplate->block;
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(pblocktemplate->block);

if(!fPoS) {
{
Expand Down Expand Up @@ -679,7 +679,8 @@ UniValue submitblock(const JSONRPCRequest& request)
"\nExamples:\n" +
HelpExampleCli("submitblock", "\"mydata\"") + HelpExampleRpc("submitblock", "\"mydata\""));

CBlock block;
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock& block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str()))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");

Expand All @@ -706,7 +707,7 @@ UniValue submitblock(const JSONRPCRequest& request)
CValidationState state;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(state, nullptr, &block, nullptr);
bool fAccepted = ProcessNewBlock(state, nullptr, blockptr, nullptr);
UnregisterValidationInterface(&sc);
if (fBlockPresent) {
if (fAccepted && !sc.found)
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/rawtransaction.cpp
Expand Up @@ -863,7 +863,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
}
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, false, !fOverrideFees)) {
if (!AcceptToMemoryPool(mempool, state, MakeTransactionRef(std::move(tx)), false, &fMissingInputs, false, !fOverrideFees)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason()));
} else {
Expand Down
11 changes: 6 additions & 5 deletions src/swifttx.cpp
Expand Up @@ -44,8 +44,9 @@ void ProcessMessageSwiftTX(CNode* pfrom, std::string& strCommand, CDataStream& v
if (strCommand == NetMsgType::IX) {
//LogPrintf("ProcessMessageSwiftTX::ix\n");
CDataStream vMsg(vRecv);
CTransaction tx;
vRecv >> tx;
CTransactionRef ptx;
vRecv >> ptx;
const CTransaction& tx = *ptx;

CInv inv(MSG_TXLOCK_REQUEST, tx.GetHash());
pfrom->AddInventoryKnown(inv);
Expand Down Expand Up @@ -75,7 +76,7 @@ void ProcessMessageSwiftTX(CNode* pfrom, std::string& strCommand, CDataStream& v
bool fAccepted = false;
{
LOCK(cs_main);
fAccepted = AcceptToMemoryPool(mempool, state, tx, true, &fMissingInputs);
fAccepted = AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs);
}
if (fAccepted) {
g_connman->RelayInv(inv);
Expand Down Expand Up @@ -260,7 +261,7 @@ int64_t CreateNewLock(CTransaction tx)
}

// check if we need to vote on this transaction
void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight)
void DoConsensusVote(const CTransaction& tx, int64_t nBlockHeight)
{
if (!fMasterNode) return;

Expand Down Expand Up @@ -400,7 +401,7 @@ bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx)
return false;
}

bool CheckForConflictingLocks(CTransaction& tx)
bool CheckForConflictingLocks(const CTransaction& tx)
{
/*
It's possible (very unlikely though) to get 2 conflicting transaction locks approved by the network.
Expand Down
4 changes: 2 additions & 2 deletions src/swifttx.h
Expand Up @@ -45,12 +45,12 @@ int64_t CreateNewLock(CTransaction tx);
bool IsIXTXValid(const CTransaction& txCollateral);

// if two conflicting locks are approved by the network, they will cancel out
bool CheckForConflictingLocks(CTransaction& tx);
bool CheckForConflictingLocks(const CTransaction& tx);

void ProcessMessageSwiftTX(CNode* pfrom, std::string& strCommand, CDataStream& vRecv);

//check if we need to vote on this transaction
void DoConsensusVote(CTransaction& tx, int64_t nBlockHeight);
void DoConsensusVote(const CTransaction& tx, int64_t nBlockHeight);

//process consensus vote message
bool ProcessConsensusVote(CNode* pnode, CConsensusVote& ctx);
Expand Down

0 comments on commit 8a9b92c

Please sign in to comment.