mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-09 11:27:28 -03:00
Compare commits
8 commits
11e4bf896a
...
e71c7c95ef
Author | SHA1 | Date | |
---|---|---|---|
|
e71c7c95ef | ||
|
66aa6a47bd | ||
|
7c123c08dd | ||
|
b2136da98d | ||
|
528b8cc98d | ||
|
89ce4b0cdb | ||
|
7f88af8b24 | ||
|
61e8d9631b |
5 changed files with 67 additions and 67 deletions
|
@ -421,6 +421,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|||
}
|
||||
|
||||
++nPackagesSelected;
|
||||
pblocktemplate->m_package_feerates.emplace_back(packageFees, static_cast<int32_t>(packageSize));
|
||||
|
||||
// Update transactions that depend on each of these
|
||||
nDescendantsUpdated += UpdatePackagesForAdded(mempool, ancestors, mapModifiedTx);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <policy/policy.h>
|
||||
#include <primitives/block.h>
|
||||
#include <txmempool.h>
|
||||
#include <util/feefrac.h>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -39,6 +40,9 @@ struct CBlockTemplate
|
|||
std::vector<CAmount> vTxFees;
|
||||
std::vector<int64_t> vTxSigOpsCost;
|
||||
std::vector<unsigned char> vchCoinbaseCommitment;
|
||||
/* A vector of package fee rates, ordered by the sequence in which
|
||||
* packages are selected for inclusion in the block template.*/
|
||||
std::vector<FeeFrac> m_package_feerates;
|
||||
};
|
||||
|
||||
// Container for tracking updates to ancestor feerate as we include (parent)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <txmempool.h>
|
||||
#include <uint256.h>
|
||||
#include <util/check.h>
|
||||
#include <util/feefrac.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <util/time.h>
|
||||
#include <util/translation.h>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <test/util/setup_common.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
|
@ -123,19 +125,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
tx.vout[0].nValue = 5000000000LL - 1000;
|
||||
// This tx has a low fee: 1000 satoshis
|
||||
Txid hashParentTx = tx.GetHash(); // save this txid for later use
|
||||
AddToMempool(tx_mempool, entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto parent_tx{entry.Fee(1000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, parent_tx);
|
||||
|
||||
// This tx has a medium fee: 10000 satoshis
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vout[0].nValue = 5000000000LL - 10000;
|
||||
Txid hashMediumFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx));
|
||||
const auto medium_fee_tx{entry.Fee(10000).Time(Now<NodeSeconds>()).SpendsCoinbase(true).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, medium_fee_tx);
|
||||
|
||||
// This tx has a high fee, but depends on the first transaction
|
||||
tx.vin[0].prevout.hash = hashParentTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
|
||||
Txid hashHighFeeTx = tx.GetHash();
|
||||
AddToMempool(tx_mempool, entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx));
|
||||
const auto high_fee_tx{entry.Fee(50000).Time(Now<NodeSeconds>()).SpendsCoinbase(false).FromTx(tx)};
|
||||
AddToMempool(tx_mempool, high_fee_tx);
|
||||
|
||||
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options);
|
||||
BOOST_REQUIRE(block_template);
|
||||
|
@ -145,6 +150,21 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
|
|||
BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx);
|
||||
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
|
||||
|
||||
// Test the inclusion of package feerates in the block template and ensure they are sequential.
|
||||
const auto block_package_feerates = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock()->m_package_feerates;
|
||||
BOOST_CHECK(block_package_feerates.size() == 2);
|
||||
|
||||
// parent_tx and high_fee_tx are added to the block as a package.
|
||||
const auto combined_txs_fee = parent_tx.GetFee() + high_fee_tx.GetFee();
|
||||
const auto combined_txs_size = parent_tx.GetTxSize() + high_fee_tx.GetTxSize();
|
||||
FeeFrac package_feefrac{combined_txs_fee, combined_txs_size};
|
||||
// The package should be added first.
|
||||
BOOST_CHECK(block_package_feerates[0] == package_feefrac);
|
||||
|
||||
// The medium_fee_tx should be added next.
|
||||
FeeFrac medium_tx_feefrac{medium_fee_tx.GetFee(), medium_fee_tx.GetTxSize()};
|
||||
BOOST_CHECK(block_package_feerates[1] == medium_tx_feefrac);
|
||||
|
||||
// Test that a package below the block min tx fee doesn't get included
|
||||
tx.vin[0].prevout.hash = hashHighFeeTx;
|
||||
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
|
||||
|
|
|
@ -2091,7 +2091,6 @@ void Chainstate::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationSta
|
|||
AssertLockHeld(cs_main);
|
||||
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
m_chainman.m_failed_blocks.insert(pindex);
|
||||
m_blockman.m_dirty_blockindex.insert(pindex);
|
||||
setBlockIndexCandidates.erase(pindex);
|
||||
InvalidChainFound(pindex);
|
||||
|
@ -3691,6 +3690,11 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||
// build a map once so that we can look up candidate blocks by chain
|
||||
// work as we go.
|
||||
std::multimap<const arith_uint256, CBlockIndex *> candidate_blocks_by_work;
|
||||
// Map to cache candidates not in the main chain that might need invalidating.
|
||||
// Maps fork block in chain to the candidates for invalidation.
|
||||
std::multimap<const CBlockIndex*, CBlockIndex*> cand_invalid_descendants;
|
||||
// Map to cache candidates for m_best_header
|
||||
std::multimap<const arith_uint256, CBlockIndex*> best_header_blocks_by_work;
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
|
@ -3707,6 +3711,12 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||
candidate->HaveNumChainTxs()) {
|
||||
candidate_blocks_by_work.insert(std::make_pair(candidate->nChainWork, candidate));
|
||||
}
|
||||
// Similarly, populate cache for blocks not in main chain to invalidate
|
||||
if (!m_chain.Contains(candidate) &&
|
||||
!CBlockIndexWorkComparator()(candidate, pindex->pprev)) {
|
||||
cand_invalid_descendants.insert(std::make_pair(m_chain.FindFork(candidate), candidate));
|
||||
best_header_blocks_by_work.insert(std::make_pair(candidate->nChainWork, candidate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3754,6 +3764,28 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||
m_blockman.m_dirty_blockindex.insert(to_mark_failed);
|
||||
}
|
||||
|
||||
// Mark descendants of the invalidated block as invalid
|
||||
auto range = cand_invalid_descendants.equal_range(invalid_walk_tip);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
it->second->nStatus |= BLOCK_FAILED_CHILD;
|
||||
}
|
||||
|
||||
// Determine new best header
|
||||
if (m_chainman.m_best_header->nStatus & BLOCK_FAILED_MASK) {
|
||||
m_chainman.m_best_header = invalid_walk_tip->pprev;
|
||||
auto best_header_it = best_header_blocks_by_work.lower_bound(m_chainman.m_best_header->nChainWork);
|
||||
while (best_header_it != best_header_blocks_by_work.end()) {
|
||||
if (best_header_it->second->nStatus & BLOCK_FAILED_MASK) {
|
||||
best_header_it = best_header_blocks_by_work.erase(best_header_it);
|
||||
} else {
|
||||
if (!CBlockIndexWorkComparator()(best_header_it->second, m_chainman.m_best_header)) {
|
||||
m_chainman.m_best_header = best_header_it->second;
|
||||
}
|
||||
++best_header_it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any equal or more work headers to setBlockIndexCandidates
|
||||
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->nChainWork);
|
||||
while (candidate_it != candidate_blocks_by_work.end()) {
|
||||
|
@ -3783,7 +3815,6 @@ bool Chainstate::InvalidateBlock(BlockValidationState& state, CBlockIndex* pinde
|
|||
to_mark_failed->nStatus |= BLOCK_FAILED_VALID;
|
||||
m_blockman.m_dirty_blockindex.insert(to_mark_failed);
|
||||
setBlockIndexCandidates.erase(to_mark_failed);
|
||||
m_chainman.m_failed_blocks.insert(to_mark_failed);
|
||||
|
||||
// If any new blocks somehow arrived while we were disconnecting
|
||||
// (above), then the pre-calculation of what should go into
|
||||
|
@ -3849,7 +3880,6 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
|
|||
// Reset invalid block marker if it was pointing to one of those.
|
||||
m_chainman.m_best_invalid = nullptr;
|
||||
}
|
||||
m_chainman.m_failed_blocks.erase(&block_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3858,7 +3888,6 @@ void Chainstate::ResetBlockFailureFlags(CBlockIndex *pindex) {
|
|||
if (pindex->nStatus & BLOCK_FAILED_MASK) {
|
||||
pindex->nStatus &= ~BLOCK_FAILED_MASK;
|
||||
m_blockman.m_dirty_blockindex.insert(pindex);
|
||||
m_chainman.m_failed_blocks.erase(pindex);
|
||||
}
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
|
@ -4362,45 +4391,6 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
|||
LogDebug(BCLog::VALIDATION, "%s: Consensus::ContextualCheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine if this block descends from any block which has been found
|
||||
* invalid (m_failed_blocks), then mark pindexPrev and any blocks between
|
||||
* them as failed. For example:
|
||||
*
|
||||
* D3
|
||||
* /
|
||||
* B2 - C2
|
||||
* / \
|
||||
* A D2 - E2 - F2
|
||||
* \
|
||||
* B1 - C1 - D1 - E1
|
||||
*
|
||||
* In the case that we attempted to reorg from E1 to F2, only to find
|
||||
* C2 to be invalid, we would mark D2, E2, and F2 as BLOCK_FAILED_CHILD
|
||||
* but NOT D3 (it was not in any of our candidate sets at the time).
|
||||
*
|
||||
* In any case D3 will also be marked as BLOCK_FAILED_CHILD at restart
|
||||
* in LoadBlockIndex.
|
||||
*/
|
||||
if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) {
|
||||
// The above does not mean "invalid": it checks if the previous block
|
||||
// hasn't been validated up to BLOCK_VALID_SCRIPTS. This is a performance
|
||||
// optimization, in the common case of adding a new block to the tip,
|
||||
// we don't need to iterate over the failed blocks list.
|
||||
for (const CBlockIndex* failedit : m_failed_blocks) {
|
||||
if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) {
|
||||
assert(failedit->nStatus & BLOCK_FAILED_VALID);
|
||||
CBlockIndex* invalid_walk = pindexPrev;
|
||||
while (invalid_walk != failedit) {
|
||||
invalid_walk->nStatus |= BLOCK_FAILED_CHILD;
|
||||
m_blockman.m_dirty_blockindex.insert(invalid_walk);
|
||||
invalid_walk = invalid_walk->pprev;
|
||||
}
|
||||
LogDebug(BCLog::VALIDATION, "header %s has prev block invalid: %s\n", hash.ToString(), block.hashPrevBlock.ToString());
|
||||
return state.Invalid(BlockValidationResult::BLOCK_INVALID_PREV, "bad-prevblk");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!min_pow_checked) {
|
||||
LogDebug(BCLog::VALIDATION, "%s: not adding new block header %s, missing anti-dos proof-of-work validation\n", __func__, hash.ToString());
|
||||
|
@ -4545,6 +4535,7 @@ bool ChainstateManager::AcceptBlock(const std::shared_ptr<const CBlock>& pblock,
|
|||
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
||||
pindex->nStatus |= BLOCK_FAILED_VALID;
|
||||
m_blockman.m_dirty_blockindex.insert(pindex);
|
||||
ActiveChainstate().InvalidChainFound(pindex);
|
||||
}
|
||||
LogError("%s: %s\n", __func__, state.ToString());
|
||||
return false;
|
||||
|
@ -5286,6 +5277,7 @@ void ChainstateManager::CheckBlockIndex()
|
|||
// are not yet validated.
|
||||
CChain best_hdr_chain;
|
||||
assert(m_best_header);
|
||||
assert(!(m_best_header->nStatus & BLOCK_FAILED_MASK));
|
||||
best_hdr_chain.SetTip(*m_best_header);
|
||||
|
||||
std::multimap<CBlockIndex*,CBlockIndex*> forward;
|
||||
|
@ -5399,6 +5391,8 @@ void ChainstateManager::CheckBlockIndex()
|
|||
if (pindexFirstInvalid == nullptr) {
|
||||
// Checks for not-invalid blocks.
|
||||
assert((pindex->nStatus & BLOCK_FAILED_MASK) == 0); // The failed mask cannot be set for blocks without invalid parents.
|
||||
} else {
|
||||
assert((pindex->nStatus & BLOCK_FAILED_MASK)); // Descendants of invalid blocks must also be marked as invalid
|
||||
}
|
||||
// Make sure m_chain_tx_count sum is correctly computed.
|
||||
if (!pindex->pprev) {
|
||||
|
@ -5412,6 +5406,8 @@ void ChainstateManager::CheckBlockIndex()
|
|||
// block, and must be set if it is.
|
||||
assert((pindex->m_chain_tx_count != 0) == (pindex == snap_base));
|
||||
}
|
||||
// There should be no block with more work than m_best_header, unless it's known to be invalid
|
||||
assert((pindex->nStatus & BLOCK_FAILED_MASK) || pindex->nChainWork <= m_best_header->nChainWork);
|
||||
|
||||
// Chainstate-specific checks on setBlockIndexCandidates
|
||||
for (auto c : GetAll()) {
|
||||
|
|
|
@ -1038,27 +1038,6 @@ public:
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* In order to efficiently track invalidity of headers, we keep the set of
|
||||
* blocks which we tried to connect and found to be invalid here (ie which
|
||||
* were set to BLOCK_FAILED_VALID since the last restart). We can then
|
||||
* walk this set and check if a new header is a descendant of something in
|
||||
* this set, preventing us from having to walk m_block_index when we try
|
||||
* to connect a bad block and fail.
|
||||
*
|
||||
* While this is more complicated than marking everything which descends
|
||||
* from an invalid block as invalid at the time we discover it to be
|
||||
* invalid, doing so would require walking all of m_block_index to find all
|
||||
* descendants. Since this case should be very rare, keeping track of all
|
||||
* BLOCK_FAILED_VALID blocks in a set should be just fine and work just as
|
||||
* well.
|
||||
*
|
||||
* Because we already walk m_block_index in height-order at startup, we go
|
||||
* ahead and mark descendants of invalid blocks as FAILED_CHILD at that time,
|
||||
* instead of putting things in this set.
|
||||
*/
|
||||
std::set<CBlockIndex*> m_failed_blocks;
|
||||
|
||||
/** Best header we've seen so far (used for getheaders queries' starting points). */
|
||||
CBlockIndex* m_best_header GUARDED_BY(::cs_main){nullptr};
|
||||
|
||||
|
|
Loading…
Reference in a new issue