Validation of pegin inputs

This commit is contained in:
Steven Roose 2018-12-12 11:44:04 +01:00
parent f4adb464e8
commit 5cbb014fe4
15 changed files with 369 additions and 79 deletions

View file

@ -16,9 +16,11 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
bool spendsCoinbase = false;
unsigned int sigOpCost = 4;
LockPoints lp;
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry(
tx, nFee, nTime, nHeight,
spendsCoinbase, sigOpCost, lp));
spendsCoinbase, sigOpCost, lp,
setPeginsSpent));
}
// Right now this is only testing eviction performance in an extremely small

View file

@ -8,6 +8,7 @@
#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <consensus/validation.h>
#include <validation.h>
// TODO remove the following dependencies
#include <chain.h>
@ -158,9 +159,18 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
for (unsigned int i = 0; i < tx.vin.size(); i++)
{
const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout);
assert(!coin.IsSpent());
const CTxOut &prevout = coin.out;
if (tx.vin[i].m_is_pegin && !IsValidPeginWitness(tx.vin[i].m_pegin_witness, tx.vin[i].prevout)) {
continue;
}
CTxOut prevout;
if (tx.vin[i].m_is_pegin) {
prevout = GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness);
} else {
const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout);
assert(!coin.IsSpent());
prevout = coin.out;
}
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags);
}
return nSigOps;
@ -215,7 +225,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
return true;
}
bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
namespace Consensus {
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent)
{
// are the actual inputs available?
if (!inputs.HaveInputs(tx)) {
@ -226,23 +237,47 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
CAmount nValueIn = 0;
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());
if (tx.vin[i].m_is_pegin) {
// Check existence and validity of pegin witness
if (!IsValidPeginWitness(tx.vin[i].m_pegin_witness, prevout)) {
return state.DoS(0, false, REJECT_PEGIN, "bad-pegin-witness");
}
std::pair<uint256, COutPoint> pegin = std::make_pair(uint256(tx.vin[i].m_pegin_witness.stack[2]), prevout);
if (inputs.IsPeginSpent(pegin)) {
return state.Invalid(false, REJECT_INVALID, "bad-txns-double-pegin", strprintf("Double-pegin of %s:%d", prevout.hash.ToString(), prevout.n));
}
if (setPeginsSpent.count(pegin)) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-double-pegin-in-obj", false,
strprintf("Double-pegin of %s:%d in single tx/block", prevout.hash.ToString(), prevout.n));
}
setPeginsSpent.insert(pegin);
// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
return state.Invalid(false,
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
}
// Tally the input amount.
const CTxOut out = GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness);
if (!MoneyRange(out.nValue)) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-pegin-inputvalue-outofrange");
}
nValueIn += out.nValue;
} else {
const Coin& coin = inputs.AccessCoin(prevout);
assert(!coin.IsSpent());
// Check for negative or overflow input values
nValueIn += coin.out.nValue;
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
// If prev is coinbase, check that it's matured
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
return state.Invalid(false,
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
}
// Check for negative or overflow input values
nValueIn += coin.out.nValue;
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
}
}
}
//TODO(rebase) you need to replace these two blocks with the `VerifyAmounts` and `HasValidFee` methods
const CAmount value_out = tx.GetValueOut();
if (nValueIn < value_out) {
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
@ -258,3 +293,4 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
txfee = txfee_aux;
return true;
}
}// namespace Consensus

View file

@ -7,13 +7,16 @@
#include <amount.h>
#include <set>
#include <stdint.h>
#include <vector>
#include <uint256.h>
class CBlockIndex;
class CCoinsViewCache;
class CTransaction;
class CValidationState;
class COutPoint;
/** Transaction validation functions */
@ -27,7 +30,7 @@ namespace Consensus {
* @param[out] txfee Set to the transaction fee if successful.
* Preconditions: tx.IsCoinBase() is false.
*/
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee);
bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, std::set<std::pair<uint256, COutPoint>>& setPeginsSpent);
} // namespace Consensus
/** Auxiliary functions for transaction validation (ideally should not be exposed) */

View file

@ -21,6 +21,8 @@ static const unsigned char REJECT_NONSTANDARD = 0x40;
// static const unsigned char REJECT_DUST = 0x41; // part of BIP 61
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
static const unsigned char REJECT_CHECKPOINT = 0x43;
// ELEMENTS:
static const unsigned char REJECT_PEGIN = 0x44;
/** Capture information about block/transaction validation */
class CValidationState {

View file

@ -207,7 +207,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
if (tx.vin[i].scriptWitness.IsNull())
continue;
const CTxOut &prev = mapInputs.AccessCoin(tx.vin[i].prevout).out;
const CTxOut &prev = tx.vin[i].m_is_pegin ? GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness) : mapInputs.AccessCoin(tx.vin[i].prevout).out;
// get the scriptPubKey corresponding to this input:
CScript prevScript = prev.scriptPubKey;

View file

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <policy/policy.h>
#include <policy/fees.h>
#include <txmempool.h>
#include <util.h>
@ -389,7 +390,8 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
/* after tx6 is mined, tx7 should move up in the sort */
std::vector<CTransactionRef> vtx;
vtx.push_back(MakeTransactionRef(tx6));
pool.removeForBlock(vtx, 1);
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
pool.removeForBlock(vtx, 1, setPeginsSpentDummy);
sortedOrder.erase(sortedOrder.begin()+1);
// Ties are broken by hash
@ -418,6 +420,93 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
CheckSort<ancestor_score>(pool, sortedOrder);
}
// ELEMENTS:
BOOST_AUTO_TEST_CASE(PeginSpentTest)
{
CBlockPolicyEstimator feeEst;
CTxMemPool pool(&feeEst);
LOCK(pool.cs);
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
TestMemPoolEntryHelper entry;
std::pair<uint256, COutPoint> pegin1, pegin2, pegin3;
GetRandBytes(pegin1.first.begin(), pegin1.first.size());
GetRandBytes(pegin2.first.begin(), pegin2.first.size());
GetRandBytes(pegin3.first.begin(), pegin3.first.size());
GetRandBytes(pegin1.second.hash.begin(), pegin1.second.hash.size());
GetRandBytes(pegin2.second.hash.begin(), pegin2.second.hash.size());
pegin3.second.hash = pegin2.second.hash;
pegin1.second.n = 0;
pegin2.second.n = 0;
pegin3.second.n = 1;
CMutableTransaction tx;
tx.vin.resize(1);
tx.vout.resize(1);
tx.vout[0].nValue = 0;
const uint256 tx1Hash(tx.GetHash());
pool.addUnchecked(tx1Hash, entry.PeginsSpent(setPeginsSpent).FromTx(tx));
BOOST_CHECK(pool.mapPeginsSpentToTxid.empty());
setPeginsSpent = {pegin1};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(2);
tx.vout[1].nValue = 0;
const uint256 tx2Hash(tx.GetHash());
pool.addUnchecked(tx2Hash, entry.PeginsSpent(setPeginsSpent).FromTx(tx));
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin1].ToString(), tx2Hash.ToString());
setPeginsSpent = {pegin2};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(3);
tx.vout[2].nValue = 0;
const uint256 tx3Hash(tx.GetHash());
pool.addUnchecked(tx3Hash, entry.PeginsSpent(setPeginsSpent).FromTx(tx));
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin2].ToString(), tx3Hash.ToString());
setPeginsSpent = {pegin3};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(4);
tx.vout[3].nValue = 0;
CTransactionRef txref(MakeTransactionRef(tx));
pool.removeForBlock({txref}, 1, setPeginsSpent);
BOOST_CHECK_EQUAL(pool.size(), 3);
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid.size(), 2);
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin1].ToString(), tx2Hash.ToString());
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin2].ToString(), tx3Hash.ToString());
setPeginsSpent = {pegin1};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(5);
tx.vout[4].nValue = 0;
txref = MakeTransactionRef(tx);
pool.removeForBlock({txref}, 2, setPeginsSpent);
BOOST_CHECK_EQUAL(pool.size(), 2);
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid.size(), 1);
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin2].ToString(), tx3Hash.ToString());
setPeginsSpent = {pegin1, pegin3};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(6);
tx.vout[5].nValue = 0;
const uint256 tx4Hash(tx.GetHash());
pool.addUnchecked(tx4Hash, entry.PeginsSpent(setPeginsSpent).FromTx(tx));
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin1].ToString(), tx4Hash.ToString());
BOOST_CHECK_EQUAL(pool.mapPeginsSpentToTxid[pegin3].ToString(), tx4Hash.ToString());
setPeginsSpent = {pegin2, pegin3};
GetRandBytes(tx.vin[0].prevout.hash.begin(), tx.vin[0].prevout.hash.size());
tx.vout.resize(7);
tx.vout[6].nValue = 0;
txref = MakeTransactionRef(tx);
pool.removeForBlock({txref}, 3, setPeginsSpent);
BOOST_CHECK_EQUAL(pool.size(), 1);
BOOST_CHECK(pool.mapPeginsSpentToTxid.empty());
}
BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
{
@ -549,7 +638,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
// ... we should keep the same min fee until we get a block
pool.removeForBlock(vtx, 1);
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
pool.removeForBlock(vtx, 1, setPeginsSpentDummy);
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
// ... then feerate should drop 1/2 each halflife

View file

@ -23,6 +23,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
CAmount basefee(2000);
CAmount deltaFee(100);
std::vector<CAmount> feeV;
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
// Populate vectors of increasing fees
for (int j = 0; j < 10; j++) {
@ -74,7 +75,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[9-h].pop_back();
}
}
mpool.removeForBlock(block, ++blocknum);
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
block.clear();
// Check after just a few txs that combining buckets works as expected
if (blocknum == 3) {
@ -113,7 +114,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
// We haven't decayed the moving average enough so we still have enough data points in every bucket
while (blocknum < 250)
mpool.removeForBlock(block, ++blocknum);
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
@ -133,7 +134,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[j].push_back(hash);
}
}
mpool.removeForBlock(block, ++blocknum);
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
}
for (int i = 1; i < 10;i++) {
@ -150,7 +151,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
txHashes[j].pop_back();
}
}
mpool.removeForBlock(block, 266);
mpool.removeForBlock(block, 266, setPeginsSpentDummy);
block.clear();
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
for (int i = 2; i < 10;i++) {
@ -171,7 +172,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
}
}
mpool.removeForBlock(block, ++blocknum);
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
block.clear();
}
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));

View file

@ -46,7 +46,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
return os;
}
BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::string& fedpegscript)
: m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
{
SHA256AutoDetect();
@ -57,6 +57,10 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
InitSignatureCache();
InitScriptExecutionCache();
fCheckBlockIndex = true;
// Hack to allow testing of fedpeg args
if (!fedpegscript.empty()) {
gArgs.SoftSetArg("-fedpegscript", fedpegscript);
}
// CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
// TODO: fix the code to support SegWit blocks.
gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
@ -78,7 +82,7 @@ fs::path BasicTestingSetup::SetDataDir(const std::string& name)
return ret;
}
TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName)
TestingSetup::TestingSetup(const std::string& chainName, const std::string& fedpegscript) : BasicTestingSetup(chainName, fedpegscript)
{
SetDataDir("tempdir");
const CChainParams& chainparams = Params();
@ -184,7 +188,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx)
{
return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
spendsCoinbase, sigOpCost, lp);
spendsCoinbase, sigOpCost, lp, setPeginsSpent);
}
/**

View file

@ -43,7 +43,7 @@ static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); }
struct BasicTestingSetup {
ECCVerifyHandle globalVerifyHandle;
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::string& fedpegscript = "");
~BasicTestingSetup();
fs::path SetDataDir(const std::string& name);
@ -69,7 +69,7 @@ struct TestingSetup: public BasicTestingSetup {
CScheduler scheduler;
std::unique_ptr<PeerLogicValidation> peerLogic;
explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::string& fedpegscript = "");
~TestingSetup();
};
@ -106,6 +106,8 @@ struct TestMemPoolEntryHelper
bool spendsCoinbase;
unsigned int sigOpCost;
LockPoints lp;
// ELEMENTS:
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
TestMemPoolEntryHelper() :
nFee(0), nTime(0), nHeight(1),
@ -120,6 +122,8 @@ struct TestMemPoolEntryHelper
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; }
// ELEMENTS:
TestMemPoolEntryHelper &PeginsSpent(std::set<std::pair<uint256, COutPoint> >& _setPeginsSpent) { setPeginsSpent = _setPeginsSpent; return *this; }
};
CBlock getBlock13b8a();

View file

@ -34,6 +34,7 @@ static const char DB_LAST_BLOCK = 'l';
// ELEMENTS:
static const char DB_PEGIN_FLAG = 'w';
static const char DB_INVALID_BLOCK_Q = 'q';
namespace {
@ -270,6 +271,14 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
return true;
}
// ELEMENTS:
bool CBlockTreeDB::ReadInvalidBlockQueue(std::vector<uint256> &vBlocks) {
return Read(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);//FIXME: why uint 56 and not ""
}
bool CBlockTreeDB::WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks) {
return Write(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);
}
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
{
std::unique_ptr<CDBIterator> pcursor(NewIterator());

View file

@ -98,6 +98,9 @@ public:
bool WriteFlag(const std::string &name, bool fValue);
bool ReadFlag(const std::string &name, bool &fValue);
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
// ELEMENTS:
bool ReadInvalidBlockQueue(std::vector<uint256> &vBlocks);
bool WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks);
};
#endif // BITCOIN_TXDB_H

View file

@ -20,9 +20,12 @@
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
bool _spendsCoinbase,
int64_t _sigOpsCost, LockPoints lp,
std::set<std::pair<uint256, COutPoint>>& _setPeginsSpent):
tx(_tx), nFee(_nFee), nTime(_nTime), entryHeight(_entryHeight),
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp),
setPeginsSpent(_setPeginsSpent)
{
nTxWeight = GetTransactionWeight(*tx);
nUsageSize = RecursiveDynamicUsage(tx);
@ -409,6 +412,12 @@ void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
newit->vTxHashesIdx = vTxHashes.size() - 1;
typedef std::pair<uint256, COutPoint> PeginPair;
for(const PeginPair& it : entry.setPeginsSpent) {
std::pair<std::map<std::pair<uint256, COutPoint>, uint256>::iterator, bool> ret = mapPeginsSpentToTxid.insert(std::make_pair(it, hash));
assert(ret.second);
}
}
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
@ -418,6 +427,12 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
for (const CTxIn& txin : it->GetTx().vin)
mapNextTx.erase(txin.prevout);
// ELEMENTS:
typedef std::pair<uint256, COutPoint> PeginPair;
for (const PeginPair& it2 : it->setPeginsSpent) {
mapPeginsSpentToTxid.erase(it2);
}
if (vTxHashes.size() > 1) {
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
@ -554,7 +569,7 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
/**
* Called when a block is connected. Removes from mempool and updates the miner fee estimator.
*/
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight)
void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight, const std::set<std::pair<uint256, COutPoint>>& setPeginsSpent)
{
LOCK(cs);
std::vector<const CTxMemPoolEntry*> entries;
@ -579,6 +594,24 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
removeConflicts(*tx);
ClearPrioritisation(tx->GetHash());
}
// ELEMENTS:
// Eject any conflicting pegins
for (std::set<std::pair<uint256, COutPoint> >::const_iterator it = setPeginsSpent.begin(); it != setPeginsSpent.end(); it++) {
std::map<std::pair<uint256, COutPoint>, uint256>::const_iterator it2 = mapPeginsSpentToTxid.find(*it);
if (it2 != mapPeginsSpentToTxid.end()) {
uint256 tx_id = it2->second;
txiter txit = mapTx.find(tx_id);
assert(txit != mapTx.end());
const CTransaction& tx = txit->GetTx();
setEntries stage;
stage.insert(txit);
RemoveStaged(stage, true);
removeRecursive(tx, MemPoolRemovalReason::CONFLICT);
ClearPrioritisation(tx_id);
}
}
lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = true;
}
@ -602,13 +635,21 @@ void CTxMemPool::clear()
_clear();
}
static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight)
static void CheckInputsAndUpdateCoins(const CTxMemPoolEntry& entry, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight, std::set<std::pair<uint256, COutPoint>>& setGlobalPeginsSpent)
{
CTransaction tx = entry.GetTx();
CValidationState state;
CAmount txfee = 0;
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee);
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee, setPeginsSpent);
assert(fCheckResult);
UpdateCoins(tx, mempoolDuplicate, 1000000);
// ELEMENTS:
assert(setPeginsSpent == entry.setPeginsSpent);
size_t prevPeginsCount = setGlobalPeginsSpent.size();
setGlobalPeginsSpent.insert(setPeginsSpent.begin(), setPeginsSpent.end());
assert(setGlobalPeginsSpent.size() == prevPeginsCount + setPeginsSpent.size());
}
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
@ -629,6 +670,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
std::list<const CTxMemPoolEntry*> waitingOnDependants;
// ELEMENTS:
std::set<std::pair<uint256, COutPoint> > setGlobalPeginsSpent;
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
unsigned int i = 0;
checkTotal += it->GetTxSize();
@ -654,7 +698,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
parentSigOpCost += it2->GetSigOpCost();
}
} else {
assert(pcoins->HaveCoin(txin.prevout));
// peg-in inputs are not sanity-checked to be valid
assert(txin.m_is_pegin || pcoins->HaveCoin(txin.prevout));
}
// Check whether its inputs are marked in mapNextTx.
auto it3 = mapNextTx.find(txin.prevout);
@ -704,7 +749,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
if (fDependsWait)
waitingOnDependants.push_back(&(*it));
else {
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
CheckInputsAndUpdateCoins(*it, mempoolDuplicate, spendheight, setGlobalPeginsSpent);
}
}
unsigned int stepsSinceLastRemove = 0;
@ -716,7 +761,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
stepsSinceLastRemove++;
assert(stepsSinceLastRemove < waitingOnDependants.size());
} else {
CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
CheckInputsAndUpdateCoins(*entry, mempoolDuplicate, spendheight, setGlobalPeginsSpent);
stepsSinceLastRemove = 0;
}
}
@ -728,6 +773,18 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
assert(&tx == it->second);
}
//
// ELEMENTS:
for (std::set<std::pair<uint256, COutPoint> >::const_iterator it = setGlobalPeginsSpent.begin(); it != setGlobalPeginsSpent.end(); it++) {
assert(!pcoins->IsPeginSpent(*it));
}
for (std::map<std::pair<uint256, COutPoint>, uint256>::const_iterator it = mapPeginsSpentToTxid.begin(); it != mapPeginsSpentToTxid.end(); it++) {
assert(setGlobalPeginsSpent.erase(it->first));
}
assert(setGlobalPeginsSpent.size() == 0);
// END ELEMENTS
//
assert(totalTxSize == checkTotal);
assert(innerUsage == cachedInnerUsage);
}

View file

@ -89,10 +89,14 @@ private:
int64_t nSigOpCostWithAncestors;
public:
// ELEMENTS:
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
bool spendsCoinbase,
int64_t nSigOpsCost, LockPoints lp);
int64_t nSigOpsCost, LockPoints lp,
std::set<std::pair<uint256, COutPoint>>& setPeginsSpent);
const CTransaction& GetTx() const { return *this->tx; }
CTransactionRef GetSharedTx() const { return this->tx; }
@ -547,7 +551,8 @@ public:
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight,
const std::set<std::pair<uint256, COutPoint>>& setPeginsSpent);
void clear();
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free

View file

@ -172,7 +172,8 @@ public:
// Block (dis)connection on a given view:
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false);
CCoinsViewCache& view, const CChainParams& chainparams,
std::set<std::pair<uint256, COutPoint>>* setPeginsSpent, bool fJustCheck = false);
// Block disconnection on our pcoinsTip:
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
@ -311,7 +312,10 @@ enum class FlushStateMode {
static bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks = nullptr);
bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs,
bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore,
PrecomputedTransactionData& txdata,
std::vector<CScriptCheck> *pvChecks = nullptr);
static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
bool CheckFinalTx(const CTransaction &tx, int flags)
@ -661,6 +665,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
{
CCoinsView dummy;
CCoinsViewCache view(&dummy);
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
LockPoints lp;
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
@ -708,7 +713,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
CAmount nFees = 0;
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) {
if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees, setPeginsSpent)) {
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
}
@ -742,7 +747,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, chainActive.Height(),
fSpendsCoinbase, nSigOpsCost, lp);
fSpendsCoinbase, nSigOpsCost, lp, setPeginsSpent);
unsigned int nSize = entry.GetTxSize();
// Check that the transaction doesn't have an excessive number of
@ -931,10 +936,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
// need to turn both off, and compare against just turning off CLEANSTACK
// to see if the failure is specifically due to witness validation.
CValidationState stateDummy; // Want reported failures to be from first CheckInputs
if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata) &&
!CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
state.SetCorruptionPossible();
if (!tx.HasWitness() && CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~(SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_CLEANSTACK), true, false, txdata)) {
if (!CheckInputs(tx, stateDummy, view, true, scriptVerifyFlags & ~SCRIPT_VERIFY_CLEANSTACK, true, false, txdata)) {
// Only the witness is missing, so the transaction itself may be fine.
state.SetCorruptionPossible();
}
}
return false; // state filled in by CheckInputs
}
@ -1352,9 +1358,16 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
if (!tx.IsCoinBase()) {
txundo.vprevout.reserve(tx.vin.size());
for (const CTxIn &txin : tx.vin) {
txundo.vprevout.emplace_back();
bool is_spent = inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
assert(is_spent);
if (txin.m_is_pegin) {
std::pair<uint256, COutPoint> outpoint = std::make_pair(uint256(txin.m_pegin_witness.stack[2]), txin.prevout);
inputs.SetPeginSpent(outpoint, true);
// Dummy undo
txundo.vprevout.emplace_back();
} else {
txundo.vprevout.emplace_back();
bool is_spent = inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
assert(is_spent);
}
}
}
// add outputs
@ -1441,7 +1454,16 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const COutPoint &prevout = tx.vin[i].prevout;
const Coin& coin = inputs.AccessCoin(prevout);
// ELEMENTS:
// If input is peg-in, create "coin" to evaluate against
Coin pegin_coin;
if (tx.vin[i].m_is_pegin) {
// Height of "output" in script evaluation will be 0
pegin_coin = Coin(GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness), 0, false);
}
const Coin& coin = tx.vin[i].m_is_pegin ? pegin_coin : inputs.AccessCoin(prevout);
assert(!coin.IsSpent());
// We very carefully only pass in things to CScriptCheck which
@ -1465,8 +1487,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
// non-upgraded nodes.
CScriptCheck check2(coin.out, tx, i,
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
if (check2())
if (check2()) {
return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError())));
}
}
// Failures of other flags indicate a transaction that is
// invalid in new blocks, e.g. an invalid P2SH. We DoS ban
@ -1577,29 +1600,43 @@ static bool AbortNode(CValidationState& state, const std::string& strMessage, co
* @param out The out point that corresponds to the tx input.
* @return A DisconnectResult as an int
*/
int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out, const CTxIn& txin, const CScriptWitness& pegin_witness)
{
bool fClean = true;
if (view.HaveCoin(out)) fClean = false; // overwriting transaction output
if (!txin.m_is_pegin) {
if (view.HaveCoin(out)) fClean = false; // overwriting transaction output
if (undo.nHeight == 0) {
// Missing undo metadata (height and coinbase). Older versions included this
// information only in undo records for the last spend of a transactions'
// outputs. This implies that it must be present for some other output of the same tx.
const Coin& alternate = AccessByTxid(view, out.hash);
if (!alternate.IsSpent()) {
undo.nHeight = alternate.nHeight;
undo.fCoinBase = alternate.fCoinBase;
if (undo.nHeight == 0) {
// Missing undo metadata (height and coinbase). Older versions included this
// information only in undo records for the last spend of a transactions'
// outputs. This implies that it must be present for some other output of the same tx.
const Coin& alternate = AccessByTxid(view, out.hash);
if (!alternate.IsSpent()) {
undo.nHeight = alternate.nHeight;
undo.fCoinBase = alternate.fCoinBase;
} else {
return DISCONNECT_FAILED; // adding output for transaction without known metadata
}
}
// The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
// sure that the coin did not already exist in the cache. As we have queried for that above
// using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
// it is an overwrite.
view.AddCoin(out, std::move(undo), !fClean);
} else {
if (!IsValidPeginWitness(pegin_witness, txin.prevout)) {
fClean = fClean && error("%s: peg-in occurred without proof", __func__);
} else {
return DISCONNECT_FAILED; // adding output for transaction without known metadata
std::pair<uint256, COutPoint> outpoint = std::make_pair(uint256(pegin_witness.stack[2]), txin.prevout);
bool fSpent = view.IsPeginSpent(outpoint);
if (!fSpent) {
fClean = fClean && error("%s: peg-in bitcoin txid not marked spent", __func__);
} else {
view.SetPeginSpent(outpoint, false);
}
}
}
// The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
// sure that the coin did not already exist in the cache. As we have queried for that above
// using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
// it is an overwrite.
view.AddCoin(out, std::move(undo), !fClean);
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
}
@ -1649,7 +1686,8 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
}
for (unsigned int j = tx.vin.size(); j-- > 0;) {
const COutPoint &out = tx.vin[j].prevout;
int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out);
const CScriptWitness& pegin_wit = tx.vin[j].m_pegin_witness;
int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out, tx.vin[j], pegin_wit);
if (res == DISCONNECT_FAILED) return DISCONNECT_FAILED;
fClean = fClean && res != DISCONNECT_UNCLEAN;
}
@ -1833,7 +1871,7 @@ static int64_t nBlocksTotal = 0;
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
* can fail if those validity checks fail (among other reasons). */
bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck)
CCoinsViewCache& view, const CChainParams& chainparams, std::set<std::pair<uint256, COutPoint>>* setPeginsSpent, bool fJustCheck)
{
AssertLockHeld(cs_main);
assert(pindex);
@ -2037,6 +2075,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
blockundo.vtxundo.reserve(block.vtx.size() - 1);
std::vector<PrecomputedTransactionData> txdata;
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
// ELEMENTS:
// Used when ConnectBlock() results are unneeded for mempool ejection
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
for (unsigned int i = 0; i < block.vtx.size(); i++)
{
const CTransaction &tx = *(block.vtx[i]);
@ -2046,7 +2089,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
if (!tx.IsCoinBase())
{
CAmount txfee = 0;
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) {
if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee,
setPeginsSpent == NULL ? setPeginsSpentDummy : *setPeginsSpent)) {
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
}
nFees += txfee;
@ -2493,13 +2537,42 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
int64_t nTime3;
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
// ELEMENTS:
// For mempool removal with pegin conflicts
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
{
CCoinsViewCache view(pcoinsTip.get());
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams);
bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams, &setPeginsSpent);
GetMainSignals().BlockChecked(blockConnecting, state);
if (!rv) {
if (state.IsInvalid())
if (state.IsInvalid()) {
InvalidBlockFound(pindexNew, state);
// ELEMENTS:
// Possibly result of RPC to mainchain bitcoind failure
// or unseen Bitcoin blocks.
// These blocks are later re-evaluated at an interval
// set by `-recheckpeginblockinterval`.
if (state.GetRejectCode() == REJECT_PEGIN) {
//Write queue of invalid blocks that
//must be cleared to continue operation
std::vector<uint256> vinvalidBlocks;
pblocktree->ReadInvalidBlockQueue(vinvalidBlocks);
bool blockAlreadyInvalid = false;
for (uint256& hash : vinvalidBlocks) {
if (hash == blockConnecting.GetHash()) {
blockAlreadyInvalid = true;
break;
}
}
if (!blockAlreadyInvalid) {
vinvalidBlocks.push_back(blockConnecting.GetHash());
pblocktree->WriteInvalidBlockQueue(vinvalidBlocks);
}
}
}
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
}
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
@ -2515,7 +2588,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
// Remove conflicting transactions from the mempool.;
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, setPeginsSpent);
disconnectpool.removeForBlock(blockConnecting.vtx);
// Update chainActive & related variables.
chainActive.SetTip(pindexNew);
@ -3642,7 +3715,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true))
if (!g_chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, NULL, true))
return false;
assert(state.IsValid());
@ -4112,7 +4185,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
CBlock block;
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams))
if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams, NULL))
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
}
}

View file

@ -3028,7 +3028,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
LockPoints lp;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp);
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp, setPeginsSpent);
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;