Skip to content

Commit

Permalink
cs_tx_val_commit_to_disk critical section changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescowens committed Dec 3, 2023
1 parent 1926b06 commit c49b2f2
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 93 deletions.
138 changes: 75 additions & 63 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,84 +1156,96 @@ EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return error("%s: TxnBegin failed", __func__);
}

if (pindexGenesisBlock == nullptr) {
if (hash != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) {
txdb.TxnAbort();
return error("%s: genesis block hash does not match", __func__);
}

pindexGenesisBlock = pindex;
} else {
assert(pindex->GetBlockHash()==block.GetHash(true));
assert(pindex->pprev == pindexBest);
{
// This lock protects the time period between the GridcoinConnectBlock, which also connects validated transaction
// contracts and causes contract handlers to fire, and the committing of the txindex changes to disk. Any contract
// handlers that generate signals whose downstream handlers make use of transaction data on disk via leveldb (txdb)
// on another thread need to take this lock to ensure that the write to leveldb and the access of the transaction data
// by the signal handlers is appropriately serialized.
LOCK(cs_tx_val_commit_to_disk);
LogPrint(BCLog::LogFlags::VOTE, "INFO: %s: cs_tx_val_commit_to_disk locked", __func__);

if (pindexGenesisBlock == nullptr) {
if (hash != (!fTestNet ? hashGenesisBlock : hashGenesisBlockTestNet)) {
txdb.TxnAbort();
return error("%s: genesis block hash does not match", __func__);
}

if (!ConnectBlock(block, txdb, pindex, false)) {
txdb.TxnAbort();
error("%s: ConnectBlock %s failed, Previous block %s",
__func__,
hash.ToString().c_str(),
pindex->pprev->GetBlockHash().ToString());
InvalidChainFound(pindex);
return false;
pindexGenesisBlock = pindex;
} else {
assert(pindex->GetBlockHash()==block.GetHash(true));
assert(pindex->pprev == pindexBest);

if (!ConnectBlock(block, txdb, pindex, false)) {
txdb.TxnAbort();
error("%s: ConnectBlock %s failed, Previous block %s",
__func__,
hash.ToString().c_str(),
pindex->pprev->GetBlockHash().ToString());
InvalidChainFound(pindex);
return false;
}
}
}

// Delete redundant memory transactions
for (auto const& tx : block.vtx) {
mempool.remove(tx);
mempool.removeConflicts(tx);
}
// Delete redundant memory transactions
for (auto const& tx : block.vtx) {
mempool.remove(tx);
mempool.removeConflicts(tx);
}

// Remove stale MRCs in the mempool that are not in this new block. Remember the MRCs were initially validated in
// AcceptToMemoryPool. Here we just need to do a staleness check.
std::vector<CTransaction> to_be_erased;
// Remove stale MRCs in the mempool that are not in this new block. Remember the MRCs were initially validated in
// AcceptToMemoryPool. Here we just need to do a staleness check.
std::vector<CTransaction> to_be_erased;

for (const auto& [_, pool_tx] : mempool.mapTx) {
for (const auto& pool_tx_contract : pool_tx.GetContracts()) {
if (pool_tx_contract.m_type == GRC::ContractType::MRC) {
GRC::MRC pool_tx_mrc = pool_tx_contract.CopyPayloadAs<GRC::MRC>();
for (const auto& [_, pool_tx] : mempool.mapTx) {
for (const auto& pool_tx_contract : pool_tx.GetContracts()) {
if (pool_tx_contract.m_type == GRC::ContractType::MRC) {
GRC::MRC pool_tx_mrc = pool_tx_contract.CopyPayloadAs<GRC::MRC>();

if (pool_tx_mrc.m_last_block_hash != hashBestChain) {
to_be_erased.push_back(pool_tx);
if (pool_tx_mrc.m_last_block_hash != hashBestChain) {
to_be_erased.push_back(pool_tx);
}
}
}
}
}

// TODO: Additional mempool removals for generic transactions based on txns...
// that satisfy lock time requirements,
// that are at least 30m old,
// that have been broadcast at least once min 5m ago,
// that had at least 45s to go in to the last block,
// and are still not in the txdb? (for the wallet itself, not mempool.)

for (const auto& tx : to_be_erased) {
LogPrintf("%s: Erasing stale transaction %s from mempool and wallet.", __func__, tx.GetHash().ToString());
mempool.remove(tx);
// If this transaction was in this wallet (i.e. erasure successful), then send signal for GUI.
if (pwalletMain->EraseFromWallet(tx.GetHash())) {
pwalletMain->NotifyTransactionChanged(pwalletMain, tx.GetHash(), CT_DELETED);
// TODO: Additional mempool removals for generic transactions based on txns...
// that satisfy lock time requirements,
// that are at least 30m old,
// that have been broadcast at least once min 5m ago,
// that had at least 45s to go in to the last block,
// and are still not in the txdb? (for the wallet itself, not mempool.)

for (const auto& tx : to_be_erased) {
LogPrintf("%s: Erasing stale transaction %s from mempool and wallet.", __func__, tx.GetHash().ToString());
mempool.remove(tx);
// If this transaction was in this wallet (i.e. erasure successful), then send signal for GUI.
if (pwalletMain->EraseFromWallet(tx.GetHash())) {
pwalletMain->NotifyTransactionChanged(pwalletMain, tx.GetHash(), CT_DELETED);
}
}
}

// Clean up spent outputs in wallet that are now not spent if mempool transactions erased above. This
// is ugly and heavyweight and should be replaced when the upstream wallet code is ported. Unlike the
// repairwallet rpc, this is silent.
if (!to_be_erased.empty()) {
int nMisMatchFound = 0;
CAmount nBalanceInQuestion = 0;
// Clean up spent outputs in wallet that are now not spent if mempool transactions erased above. This
// is ugly and heavyweight and should be replaced when the upstream wallet code is ported. Unlike the
// repairwallet rpc, this is silent.
if (!to_be_erased.empty()) {
int nMisMatchFound = 0;
CAmount nBalanceInQuestion = 0;

pwalletMain->FixSpentCoins(nMisMatchFound, nBalanceInQuestion);
}
pwalletMain->FixSpentCoins(nMisMatchFound, nBalanceInQuestion);
}

if (!txdb.WriteHashBestChain(pindex->GetBlockHash())) {
txdb.TxnAbort();
return error("%s: WriteHashBestChain failed", __func__);
}
if (!txdb.WriteHashBestChain(pindex->GetBlockHash())) {
txdb.TxnAbort();
return error("%s: WriteHashBestChain failed", __func__);
}

// Make sure it's successfully written to disk before changing memory structure
if (!txdb.TxnCommit()) {
return error("%s: TxnCommit failed", __func__);
}

// Make sure it's successfully written to disk before changing memory structure
if (!txdb.TxnCommit()) {
return error("%s: TxnCommit failed", __func__);
LogPrint(BCLog::LogFlags::VOTE, "INFO: %s: cs_tx_val_commit_to_disk unlocked", __func__);
}

// Add to current best branch
Expand Down
48 changes: 18 additions & 30 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1641,42 +1641,30 @@ bool ConnectBlock(CBlock& block, CTxDB& txdb, CBlockIndex* pindex, bool fJustChe
mapQueuedChanges[hashTx] = CTxIndex(posThisTx, tx.vout.size());
}

if (IsResearchAgeEnabled(pindex->nHeight)
&& !GridcoinConnectBlock(block, pindex, txdb, stake_value_in, nStakeReward, nFees))
{
// This lock protects the time period between the GridcoinConnectBlock, which also connects validated transaction contracts
// and causes contract handlers to fire, and the committing of the txindex changes to disk below. Any contract handlers that
// generate signals whose downstream handlers make use of transaction data on disk via leveldb (txdb) on another thread need
// to take this lock to ensure that the write to leveldb and the access of the transaction data by the signal handlers is
// appropriately serialized.
LOCK(cs_tx_val_commit_to_disk);
LogPrint(BCLog::LogFlags::VOTE, "INFO: %s: cs_tx_val_commit_to_disk locked", __func__);

if (IsResearchAgeEnabled(pindex->nHeight)
&& !GridcoinConnectBlock(block, pindex, txdb, stake_value_in, nStakeReward, nFees))
{
return false;
}

pindex->nMoneySupply = ReturnCurrentMoneySupply(pindex) + nValueOut - nValueIn;
return false;
}

if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex)))
return error("%s: WriteBlockIndex for pindex failed", __func__);
pindex->nMoneySupply = ReturnCurrentMoneySupply(pindex) + nValueOut - nValueIn;

if (!OutOfSyncByAge())
{
fColdBoot = false;
}
if (!txdb.WriteBlockIndex(CDiskBlockIndex(pindex)))
return error("%s: WriteBlockIndex for pindex failed", __func__);

if (fJustCheck)
return true;
if (!OutOfSyncByAge())
{
fColdBoot = false;
}

// Write queued txindex changes
for (const auto& [hash, index] : mapQueuedChanges)
{
if (!txdb.UpdateTxIndex(hash, index))
return error("%s: UpdateTxIndex failed", __func__);
}
if (fJustCheck)
return true;

LogPrint(BCLog::LogFlags::VOTE, "INFO: %s: cs_tx_val_commit_to_disk unlocked", __func__);
// Write queued txindex changes
for (const auto& [hash, index] : mapQueuedChanges)
{
if (!txdb.UpdateTxIndex(hash, index))
return error("%s: UpdateTxIndex failed", __func__);
}

// Update block index on disk without changing it in memory.
Expand Down

0 comments on commit c49b2f2

Please sign in to comment.