mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
e0f0d865e2
## Issue being fixed or feature implemented
As discovered during platform testing by @shumkov , it seems as the
chain can halt in miner if somehow mempool would have several
transactions that are somehow invalid (maybe too low fee or something
else). They can't be mined, but miner can't prepare a valid block with
correct Credit Pool amount.
It is indeed can happen although I haven't reproduced it with functional
tests at the moment 🤷♂️
## What was done?
Refactored and simplified a logic of Credit Pool amount of validation
and added one more layer of validation: after all transaction are
actually added to block by miner, it is recalculated one more time.
Also used correct `pindexPrev` instead Tip() for EHF signals.
## How Has This Been Tested?
Before this changes platform failed with this error and chain halt:
```
2023-10-20T06:20:16Z (mocktime: 2023-10-20T06:28:29Z) ERROR: ConnectBlock(DASH): CheckCreditPoolDiffForBlock for block 9d635e1fd0d7a8a5bf16ce158d3a39cbf903864bb6d671769836ea7db6055230 failed with bad-cbtx-asse locked-amount
```
With changes from this PR platform is generate the asset-lock
transactions that are included to block and chain is not halt:
```
2023-10-27T10:45:37Z (mocktime: 2023-10-27T14:37:22Z) GetCreditPoolDiffForBlock: CCreditPool is CCreditPool(locked=32100015, currentLimit=32100015)
```
unit/functional tests are succeed.
## Breaking Changes
N/A; no consensus rules are changed
## Checklist:
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [ ] I have added or updated relevant unit/integration/functional/e2e
tests
- [ ] I have made corresponding changes to the documentation
- [x] I have assigned this pull request to a milestone
---------
Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com>
Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
225 lines
8.4 KiB
C++
225 lines
8.4 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef BITCOIN_MINER_H
|
|
#define BITCOIN_MINER_H
|
|
|
|
#include <primitives/block.h>
|
|
#include <txmempool.h>
|
|
#include <validation.h>
|
|
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <stdint.h>
|
|
|
|
#include <boost/multi_index_container.hpp>
|
|
#include <boost/multi_index/ordered_index.hpp>
|
|
|
|
class CBlockIndex;
|
|
class CChainParams;
|
|
class CConnman;
|
|
class CGovernanceManager;
|
|
class CScript;
|
|
class CSporkManager;
|
|
struct LLMQContext;
|
|
|
|
namespace Consensus { struct Params; };
|
|
namespace llmq {
|
|
class CChainLocksHandler;
|
|
class CInstantSendManager;
|
|
class CQuorumBlockProcessor;
|
|
} // namespace llmq
|
|
|
|
static const bool DEFAULT_PRINTPRIORITY = false;
|
|
|
|
struct CBlockTemplate
|
|
{
|
|
CBlock block;
|
|
std::vector<CAmount> vTxFees;
|
|
std::vector<int64_t> vTxSigOps;
|
|
uint32_t nPrevBits; // nBits of previous block (for subsidy calculation)
|
|
std::vector<CTxOut> voutMasternodePayments; // masternode payment
|
|
std::vector<CTxOut> voutSuperblockPayments; // superblock payment
|
|
};
|
|
|
|
// Container for tracking updates to ancestor feerate as we include (parent)
|
|
// transactions in a block
|
|
struct CTxMemPoolModifiedEntry {
|
|
explicit CTxMemPoolModifiedEntry(CTxMemPool::txiter entry)
|
|
{
|
|
iter = entry;
|
|
nSizeWithAncestors = entry->GetSizeWithAncestors();
|
|
nModFeesWithAncestors = entry->GetModFeesWithAncestors();
|
|
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors();
|
|
}
|
|
|
|
int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
|
|
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
|
|
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
|
|
size_t GetTxSize() const { return iter->GetTxSize(); }
|
|
const CTransaction& GetTx() const { return iter->GetTx(); }
|
|
|
|
CTxMemPool::txiter iter;
|
|
uint64_t nSizeWithAncestors;
|
|
CAmount nModFeesWithAncestors;
|
|
unsigned int nSigOpCountWithAncestors;
|
|
};
|
|
|
|
/** Comparator for CTxMemPool::txiter objects.
|
|
* It simply compares the internal memory address of the CTxMemPoolEntry object
|
|
* pointed to. This means it has no meaning, and is only useful for using them
|
|
* as key in other indexes.
|
|
*/
|
|
struct CompareCTxMemPoolIter {
|
|
bool operator()(const CTxMemPool::txiter& a, const CTxMemPool::txiter& b) const
|
|
{
|
|
return &(*a) < &(*b);
|
|
}
|
|
};
|
|
|
|
struct modifiedentry_iter {
|
|
typedef CTxMemPool::txiter result_type;
|
|
result_type operator() (const CTxMemPoolModifiedEntry &entry) const
|
|
{
|
|
return entry.iter;
|
|
}
|
|
};
|
|
|
|
// A comparator that sorts transactions based on number of ancestors.
|
|
// This is sufficient to sort an ancestor package in an order that is valid
|
|
// to appear in a block.
|
|
struct CompareTxIterByAncestorCount {
|
|
bool operator()(const CTxMemPool::txiter &a, const CTxMemPool::txiter &b) const
|
|
{
|
|
if (a->GetCountWithAncestors() != b->GetCountWithAncestors())
|
|
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
|
|
return CTxMemPool::CompareIteratorByHash()(a, b);
|
|
}
|
|
};
|
|
|
|
typedef boost::multi_index_container<
|
|
CTxMemPoolModifiedEntry,
|
|
boost::multi_index::indexed_by<
|
|
boost::multi_index::ordered_unique<
|
|
modifiedentry_iter,
|
|
CompareCTxMemPoolIter
|
|
>,
|
|
// sorted by modified ancestor fee rate
|
|
boost::multi_index::ordered_non_unique<
|
|
// Reuse same tag from CTxMemPool's similar index
|
|
boost::multi_index::tag<ancestor_score>,
|
|
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
|
|
CompareTxMemPoolEntryByAncestorFee
|
|
>
|
|
>
|
|
> indexed_modified_transaction_set;
|
|
|
|
typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter;
|
|
typedef indexed_modified_transaction_set::index<ancestor_score>::type::iterator modtxscoreiter;
|
|
|
|
struct update_for_parent_inclusion
|
|
{
|
|
explicit update_for_parent_inclusion(CTxMemPool::txiter it) : iter(it) {}
|
|
|
|
void operator() (CTxMemPoolModifiedEntry &e)
|
|
{
|
|
e.nModFeesWithAncestors -= iter->GetFee();
|
|
e.nSizeWithAncestors -= iter->GetTxSize();
|
|
e.nSigOpCountWithAncestors -= iter->GetSigOpCount();
|
|
}
|
|
|
|
CTxMemPool::txiter iter;
|
|
};
|
|
|
|
/** Generate a new block, without valid proof-of-work */
|
|
class BlockAssembler
|
|
{
|
|
private:
|
|
// The constructed block template
|
|
std::unique_ptr<CBlockTemplate> pblocktemplate;
|
|
|
|
// Configuration parameters for the block size
|
|
unsigned int nBlockMaxSize;
|
|
CFeeRate blockMinFeeRate;
|
|
|
|
// Information on the current status of the block
|
|
uint64_t nBlockSize;
|
|
uint64_t nBlockTx;
|
|
unsigned int nBlockSigOps;
|
|
CAmount nFees;
|
|
CTxMemPool::setEntries inBlock;
|
|
|
|
// Chain context for the block
|
|
int nHeight;
|
|
int64_t nLockTimeCutoff;
|
|
const CChainParams& chainparams;
|
|
const CTxMemPool& m_mempool;
|
|
CChainState& m_chainstate;
|
|
const CSporkManager& spork_manager;
|
|
CGovernanceManager& governance_manager;
|
|
const llmq::CQuorumBlockProcessor& quorum_block_processor;
|
|
llmq::CChainLocksHandler& m_clhandler;
|
|
llmq::CInstantSendManager& m_isman;
|
|
CEvoDB& m_evoDb;
|
|
|
|
public:
|
|
struct Options {
|
|
Options();
|
|
size_t nBlockMaxSize;
|
|
CFeeRate blockMinFeeRate;
|
|
};
|
|
|
|
explicit BlockAssembler(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
|
|
LLMQContext& llmq_ctx, CEvoDB& evoDb, CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params);
|
|
explicit BlockAssembler(const CSporkManager& sporkManager, CGovernanceManager& governanceManager,
|
|
LLMQContext& llmq_ctx, CEvoDB& evoDb, CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
|
|
|
|
/** Construct a new block template with coinbase to scriptPubKeyIn */
|
|
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
|
|
|
inline static std::optional<int64_t> m_last_block_num_txs{};
|
|
inline static std::optional<int64_t> m_last_block_size{};
|
|
|
|
private:
|
|
// utility functions
|
|
/** Clear the block's state and prepare for assembling a new block */
|
|
void resetBlock();
|
|
/** Add a tx to the block */
|
|
void AddToBlock(CTxMemPool::txiter iter);
|
|
|
|
// Methods for how to add transactions to a block.
|
|
/** Add transactions based on feerate including unconfirmed ancestors
|
|
* Increments nPackagesSelected / nDescendantsUpdated with corresponding
|
|
* statistics from the package selection (for logging statistics). */
|
|
void addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated,
|
|
const CBlockIndex* pindexPrev) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
|
|
// helper functions for addPackageTxs()
|
|
/** Remove confirmed (inBlock) entries from given set */
|
|
void onlyUnconfirmed(CTxMemPool::setEntries& testSet);
|
|
/** Test if a new package would "fit" in the block */
|
|
bool TestPackage(uint64_t packageSize, unsigned int packageSigOps) const;
|
|
/** Perform checks on each transaction in a package:
|
|
* locktime
|
|
* These checks should always succeed, and they're here
|
|
* only as an extra check in case of suboptimal node configuration */
|
|
bool TestPackageTransactions(const CTxMemPool::setEntries& package) const;
|
|
/** Return true if given transaction from mapTx has already been evaluated,
|
|
* or if the transaction's cached data in mapTx is incorrect. */
|
|
bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set& mapModifiedTx, CTxMemPool::setEntries& failedTx) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
/** Sort the package in an order that is valid to appear in a block */
|
|
void SortForBlock(const CTxMemPool::setEntries& package, std::vector<CTxMemPool::txiter>& sortedEntries);
|
|
/** Add descendants of given transactions to mapModifiedTx with ancestor
|
|
* state updated assuming given transactions are inBlock. Returns number
|
|
* of updated descendants. */
|
|
int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set& mapModifiedTx) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
};
|
|
|
|
/** Modify the extranonce in a block */
|
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
|
int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev);
|
|
|
|
#endif // BITCOIN_MINER_H
|