mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Validation of pegin inputs
This commit is contained in:
parent
f4adb464e8
commit
5cbb014fe4
15 changed files with 369 additions and 79 deletions
|
|
@ -16,9 +16,11 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
|
||||||
bool spendsCoinbase = false;
|
bool spendsCoinbase = false;
|
||||||
unsigned int sigOpCost = 4;
|
unsigned int sigOpCost = 4;
|
||||||
LockPoints lp;
|
LockPoints lp;
|
||||||
|
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
|
||||||
pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry(
|
pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry(
|
||||||
tx, nFee, nTime, nHeight,
|
tx, nFee, nTime, nHeight,
|
||||||
spendsCoinbase, sigOpCost, lp));
|
spendsCoinbase, sigOpCost, lp,
|
||||||
|
setPeginsSpent));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right now this is only testing eviction performance in an extremely small
|
// Right now this is only testing eviction performance in an extremely small
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <script/interpreter.h>
|
#include <script/interpreter.h>
|
||||||
#include <consensus/validation.h>
|
#include <consensus/validation.h>
|
||||||
|
#include <validation.h>
|
||||||
|
|
||||||
// TODO remove the following dependencies
|
// TODO remove the following dependencies
|
||||||
#include <chain.h>
|
#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++)
|
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||||
{
|
{
|
||||||
const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout);
|
if (tx.vin[i].m_is_pegin && !IsValidPeginWitness(tx.vin[i].m_pegin_witness, tx.vin[i].prevout)) {
|
||||||
assert(!coin.IsSpent());
|
continue;
|
||||||
const CTxOut &prevout = coin.out;
|
}
|
||||||
|
|
||||||
|
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);
|
nSigOps += CountWitnessSigOps(tx.vin[i].scriptSig, prevout.scriptPubKey, &tx.vin[i].scriptWitness, flags);
|
||||||
}
|
}
|
||||||
return nSigOps;
|
return nSigOps;
|
||||||
|
|
@ -215,7 +225,8 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe
|
||||||
return true;
|
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?
|
// are the actual inputs available?
|
||||||
if (!inputs.HaveInputs(tx)) {
|
if (!inputs.HaveInputs(tx)) {
|
||||||
|
|
@ -226,23 +237,47 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||||
CAmount nValueIn = 0;
|
CAmount nValueIn = 0;
|
||||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
const COutPoint &prevout = tx.vin[i].prevout;
|
||||||
const Coin& coin = inputs.AccessCoin(prevout);
|
if (tx.vin[i].m_is_pegin) {
|
||||||
assert(!coin.IsSpent());
|
// 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
|
// Tally the input amount.
|
||||||
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
const CTxOut out = GetPeginOutputFromWitness(tx.vin[i].m_pegin_witness);
|
||||||
return state.Invalid(false,
|
if (!MoneyRange(out.nValue)) {
|
||||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
|
return state.DoS(100, false, REJECT_INVALID, "bad-txns-pegin-inputvalue-outofrange");
|
||||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
}
|
||||||
}
|
nValueIn += out.nValue;
|
||||||
|
} else {
|
||||||
|
const Coin& coin = inputs.AccessCoin(prevout);
|
||||||
|
assert(!coin.IsSpent());
|
||||||
|
|
||||||
// Check for negative or overflow input values
|
// If prev is coinbase, check that it's matured
|
||||||
nValueIn += coin.out.nValue;
|
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) {
|
||||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
return state.Invalid(false,
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
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();
|
const CAmount value_out = tx.GetValueOut();
|
||||||
if (nValueIn < value_out) {
|
if (nValueIn < value_out) {
|
||||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false,
|
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;
|
txfee = txfee_aux;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}// namespace Consensus
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,16 @@
|
||||||
|
|
||||||
#include <amount.h>
|
#include <amount.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <uint256.h>
|
||||||
|
|
||||||
class CBlockIndex;
|
class CBlockIndex;
|
||||||
class CCoinsViewCache;
|
class CCoinsViewCache;
|
||||||
class CTransaction;
|
class CTransaction;
|
||||||
class CValidationState;
|
class CValidationState;
|
||||||
|
class COutPoint;
|
||||||
|
|
||||||
/** Transaction validation functions */
|
/** Transaction validation functions */
|
||||||
|
|
||||||
|
|
@ -27,7 +30,7 @@ namespace Consensus {
|
||||||
* @param[out] txfee Set to the transaction fee if successful.
|
* @param[out] txfee Set to the transaction fee if successful.
|
||||||
* Preconditions: tx.IsCoinBase() is false.
|
* 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
|
} // namespace Consensus
|
||||||
|
|
||||||
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
||||||
|
|
|
||||||
|
|
@ -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_DUST = 0x41; // part of BIP 61
|
||||||
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
static const unsigned char REJECT_INSUFFICIENTFEE = 0x42;
|
||||||
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
static const unsigned char REJECT_CHECKPOINT = 0x43;
|
||||||
|
// ELEMENTS:
|
||||||
|
static const unsigned char REJECT_PEGIN = 0x44;
|
||||||
|
|
||||||
/** Capture information about block/transaction validation */
|
/** Capture information about block/transaction validation */
|
||||||
class CValidationState {
|
class CValidationState {
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
|
||||||
if (tx.vin[i].scriptWitness.IsNull())
|
if (tx.vin[i].scriptWitness.IsNull())
|
||||||
continue;
|
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:
|
// get the scriptPubKey corresponding to this input:
|
||||||
CScript prevScript = prev.scriptPubKey;
|
CScript prevScript = prev.scriptPubKey;
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <policy/policy.h>
|
#include <policy/policy.h>
|
||||||
|
#include <policy/fees.h>
|
||||||
#include <txmempool.h>
|
#include <txmempool.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
|
|
||||||
|
|
@ -389,7 +390,8 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
|
||||||
/* after tx6 is mined, tx7 should move up in the sort */
|
/* after tx6 is mined, tx7 should move up in the sort */
|
||||||
std::vector<CTransactionRef> vtx;
|
std::vector<CTransactionRef> vtx;
|
||||||
vtx.push_back(MakeTransactionRef(tx6));
|
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);
|
sortedOrder.erase(sortedOrder.begin()+1);
|
||||||
// Ties are broken by hash
|
// Ties are broken by hash
|
||||||
|
|
@ -418,6 +420,93 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
|
||||||
CheckSort<ancestor_score>(pool, sortedOrder);
|
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)
|
BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
{
|
{
|
||||||
|
|
@ -549,7 +638,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest)
|
||||||
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
|
SetMockTime(42 + CTxMemPool::ROLLING_FEE_HALFLIFE);
|
||||||
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
|
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), maxFeeRateRemoved.GetFeePerK() + 1000);
|
||||||
// ... we should keep the same min fee until we get a block
|
// ... 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);
|
SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE);
|
||||||
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
|
BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0));
|
||||||
// ... then feerate should drop 1/2 each halflife
|
// ... then feerate should drop 1/2 each halflife
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||||
CAmount basefee(2000);
|
CAmount basefee(2000);
|
||||||
CAmount deltaFee(100);
|
CAmount deltaFee(100);
|
||||||
std::vector<CAmount> feeV;
|
std::vector<CAmount> feeV;
|
||||||
|
std::set<std::pair<uint256, COutPoint>> setPeginsSpentDummy;
|
||||||
|
|
||||||
// Populate vectors of increasing fees
|
// Populate vectors of increasing fees
|
||||||
for (int j = 0; j < 10; j++) {
|
for (int j = 0; j < 10; j++) {
|
||||||
|
|
@ -74,7 +75,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||||
txHashes[9-h].pop_back();
|
txHashes[9-h].pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpool.removeForBlock(block, ++blocknum);
|
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
|
||||||
block.clear();
|
block.clear();
|
||||||
// Check after just a few txs that combining buckets works as expected
|
// Check after just a few txs that combining buckets works as expected
|
||||||
if (blocknum == 3) {
|
if (blocknum == 3) {
|
||||||
|
|
@ -113,7 +114,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||||
// Mine 50 more blocks with no transactions happening, estimates shouldn't change
|
// 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
|
// We haven't decayed the moving average enough so we still have enough data points in every bucket
|
||||||
while (blocknum < 250)
|
while (blocknum < 250)
|
||||||
mpool.removeForBlock(block, ++blocknum);
|
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
|
||||||
|
|
||||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||||
for (int i = 2; i < 10;i++) {
|
for (int i = 2; i < 10;i++) {
|
||||||
|
|
@ -133,7 +134,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||||
txHashes[j].push_back(hash);
|
txHashes[j].push_back(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpool.removeForBlock(block, ++blocknum);
|
mpool.removeForBlock(block, ++blocknum, setPeginsSpentDummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < 10;i++) {
|
for (int i = 1; i < 10;i++) {
|
||||||
|
|
@ -150,7 +151,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates)
|
||||||
txHashes[j].pop_back();
|
txHashes[j].pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mpool.removeForBlock(block, 266);
|
mpool.removeForBlock(block, 266, setPeginsSpentDummy);
|
||||||
block.clear();
|
block.clear();
|
||||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||||
for (int i = 2; i < 10;i++) {
|
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();
|
block.clear();
|
||||||
}
|
}
|
||||||
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
BOOST_CHECK(feeEst.estimateFee(1) == CFeeRate(0));
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ std::ostream& operator<<(std::ostream& os, const uint256& num)
|
||||||
return os;
|
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))))
|
: m_path_root(fs::temp_directory_path() / "test_bitcoin" / strprintf("%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(1 << 30))))
|
||||||
{
|
{
|
||||||
SHA256AutoDetect();
|
SHA256AutoDetect();
|
||||||
|
|
@ -57,6 +57,10 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
|
||||||
InitSignatureCache();
|
InitSignatureCache();
|
||||||
InitScriptExecutionCache();
|
InitScriptExecutionCache();
|
||||||
fCheckBlockIndex = true;
|
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.
|
// CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests.
|
||||||
// TODO: fix the code to support SegWit blocks.
|
// TODO: fix the code to support SegWit blocks.
|
||||||
gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT));
|
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;
|
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");
|
SetDataDir("tempdir");
|
||||||
const CChainParams& chainparams = Params();
|
const CChainParams& chainparams = Params();
|
||||||
|
|
@ -184,7 +188,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
|
||||||
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx)
|
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx)
|
||||||
{
|
{
|
||||||
return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
|
return CTxMemPoolEntry(tx, nFee, nTime, nHeight,
|
||||||
spendsCoinbase, sigOpCost, lp);
|
spendsCoinbase, sigOpCost, lp, setPeginsSpent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ static inline bool InsecureRandBool() { return insecure_rand_ctx.randbool(); }
|
||||||
struct BasicTestingSetup {
|
struct BasicTestingSetup {
|
||||||
ECCVerifyHandle globalVerifyHandle;
|
ECCVerifyHandle globalVerifyHandle;
|
||||||
|
|
||||||
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
explicit BasicTestingSetup(const std::string& chainName = CBaseChainParams::MAIN, const std::string& fedpegscript = "");
|
||||||
~BasicTestingSetup();
|
~BasicTestingSetup();
|
||||||
|
|
||||||
fs::path SetDataDir(const std::string& name);
|
fs::path SetDataDir(const std::string& name);
|
||||||
|
|
@ -69,7 +69,7 @@ struct TestingSetup: public BasicTestingSetup {
|
||||||
CScheduler scheduler;
|
CScheduler scheduler;
|
||||||
std::unique_ptr<PeerLogicValidation> peerLogic;
|
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();
|
~TestingSetup();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -106,6 +106,8 @@ struct TestMemPoolEntryHelper
|
||||||
bool spendsCoinbase;
|
bool spendsCoinbase;
|
||||||
unsigned int sigOpCost;
|
unsigned int sigOpCost;
|
||||||
LockPoints lp;
|
LockPoints lp;
|
||||||
|
// ELEMENTS:
|
||||||
|
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
|
||||||
|
|
||||||
TestMemPoolEntryHelper() :
|
TestMemPoolEntryHelper() :
|
||||||
nFee(0), nTime(0), nHeight(1),
|
nFee(0), nTime(0), nHeight(1),
|
||||||
|
|
@ -120,6 +122,8 @@ struct TestMemPoolEntryHelper
|
||||||
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
TestMemPoolEntryHelper &Height(unsigned int _height) { nHeight = _height; return *this; }
|
||||||
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; }
|
||||||
TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; 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();
|
CBlock getBlock13b8a();
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ static const char DB_LAST_BLOCK = 'l';
|
||||||
|
|
||||||
// ELEMENTS:
|
// ELEMENTS:
|
||||||
static const char DB_PEGIN_FLAG = 'w';
|
static const char DB_PEGIN_FLAG = 'w';
|
||||||
|
static const char DB_INVALID_BLOCK_Q = 'q';
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
|
@ -270,6 +271,14 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
|
||||||
return true;
|
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)
|
bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
|
||||||
{
|
{
|
||||||
std::unique_ptr<CDBIterator> pcursor(NewIterator());
|
std::unique_ptr<CDBIterator> pcursor(NewIterator());
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ public:
|
||||||
bool WriteFlag(const std::string &name, bool fValue);
|
bool WriteFlag(const std::string &name, bool fValue);
|
||||||
bool ReadFlag(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);
|
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
|
#endif // BITCOIN_TXDB_H
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@
|
||||||
|
|
||||||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, unsigned int _entryHeight,
|
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),
|
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);
|
nTxWeight = GetTransactionWeight(*tx);
|
||||||
nUsageSize = RecursiveDynamicUsage(tx);
|
nUsageSize = RecursiveDynamicUsage(tx);
|
||||||
|
|
@ -409,6 +412,12 @@ void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||||
|
|
||||||
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
||||||
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
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)
|
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)
|
for (const CTxIn& txin : it->GetTx().vin)
|
||||||
mapNextTx.erase(txin.prevout);
|
mapNextTx.erase(txin.prevout);
|
||||||
|
|
||||||
|
// ELEMENTS:
|
||||||
|
typedef std::pair<uint256, COutPoint> PeginPair;
|
||||||
|
for (const PeginPair& it2 : it->setPeginsSpent) {
|
||||||
|
mapPeginsSpentToTxid.erase(it2);
|
||||||
|
}
|
||||||
|
|
||||||
if (vTxHashes.size() > 1) {
|
if (vTxHashes.size() > 1) {
|
||||||
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
|
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
|
||||||
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
|
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.
|
* 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);
|
LOCK(cs);
|
||||||
std::vector<const CTxMemPoolEntry*> entries;
|
std::vector<const CTxMemPoolEntry*> entries;
|
||||||
|
|
@ -579,6 +594,24 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
||||||
removeConflicts(*tx);
|
removeConflicts(*tx);
|
||||||
ClearPrioritisation(tx->GetHash());
|
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();
|
lastRollingFeeUpdate = GetTime();
|
||||||
blockSinceLastRollingFeeBump = true;
|
blockSinceLastRollingFeeBump = true;
|
||||||
}
|
}
|
||||||
|
|
@ -602,13 +635,21 @@ void CTxMemPool::clear()
|
||||||
_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;
|
CValidationState state;
|
||||||
CAmount txfee = 0;
|
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);
|
assert(fCheckResult);
|
||||||
UpdateCoins(tx, mempoolDuplicate, 1000000);
|
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
|
void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
|
|
@ -629,6 +670,9 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
|
const int64_t spendheight = GetSpendHeight(mempoolDuplicate);
|
||||||
|
|
||||||
std::list<const CTxMemPoolEntry*> waitingOnDependants;
|
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++) {
|
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
checkTotal += it->GetTxSize();
|
checkTotal += it->GetTxSize();
|
||||||
|
|
@ -654,7 +698,8 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
parentSigOpCost += it2->GetSigOpCost();
|
parentSigOpCost += it2->GetSigOpCost();
|
||||||
}
|
}
|
||||||
} else {
|
} 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.
|
// Check whether its inputs are marked in mapNextTx.
|
||||||
auto it3 = mapNextTx.find(txin.prevout);
|
auto it3 = mapNextTx.find(txin.prevout);
|
||||||
|
|
@ -704,7 +749,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
if (fDependsWait)
|
if (fDependsWait)
|
||||||
waitingOnDependants.push_back(&(*it));
|
waitingOnDependants.push_back(&(*it));
|
||||||
else {
|
else {
|
||||||
CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight);
|
CheckInputsAndUpdateCoins(*it, mempoolDuplicate, spendheight, setGlobalPeginsSpent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsigned int stepsSinceLastRemove = 0;
|
unsigned int stepsSinceLastRemove = 0;
|
||||||
|
|
@ -716,7 +761,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
stepsSinceLastRemove++;
|
stepsSinceLastRemove++;
|
||||||
assert(stepsSinceLastRemove < waitingOnDependants.size());
|
assert(stepsSinceLastRemove < waitingOnDependants.size());
|
||||||
} else {
|
} else {
|
||||||
CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight);
|
CheckInputsAndUpdateCoins(*entry, mempoolDuplicate, spendheight, setGlobalPeginsSpent);
|
||||||
stepsSinceLastRemove = 0;
|
stepsSinceLastRemove = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -728,6 +773,18 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
assert(&tx == it->second);
|
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(totalTxSize == checkTotal);
|
||||||
assert(innerUsage == cachedInnerUsage);
|
assert(innerUsage == cachedInnerUsage);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,14 @@ private:
|
||||||
int64_t nSigOpCostWithAncestors;
|
int64_t nSigOpCostWithAncestors;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// ELEMENTS:
|
||||||
|
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
|
||||||
|
|
||||||
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
|
||||||
int64_t _nTime, unsigned int _entryHeight,
|
int64_t _nTime, unsigned int _entryHeight,
|
||||||
bool spendsCoinbase,
|
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; }
|
const CTransaction& GetTx() const { return *this->tx; }
|
||||||
CTransactionRef GetSharedTx() 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 removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN);
|
||||||
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags);
|
||||||
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
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();
|
||||||
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
|
void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,8 @@ public:
|
||||||
// Block (dis)connection on a given view:
|
// Block (dis)connection on a given view:
|
||||||
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
|
DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view);
|
||||||
bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
|
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:
|
// Block disconnection on our pcoinsTip:
|
||||||
bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool);
|
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 bool FlushStateToDisk(const CChainParams& chainParams, CValidationState &state, FlushStateMode mode, int nManualPruneHeight=0);
|
||||||
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
|
static void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight);
|
||||||
static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight);
|
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);
|
static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false);
|
||||||
|
|
||||||
bool CheckFinalTx(const CTransaction &tx, int flags)
|
bool CheckFinalTx(const CTransaction &tx, int flags)
|
||||||
|
|
@ -661,6 +665,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
|
||||||
{
|
{
|
||||||
CCoinsView dummy;
|
CCoinsView dummy;
|
||||||
CCoinsViewCache view(&dummy);
|
CCoinsViewCache view(&dummy);
|
||||||
|
std::set<std::pair<uint256, COutPoint> > setPeginsSpent;
|
||||||
|
|
||||||
LockPoints lp;
|
LockPoints lp;
|
||||||
CCoinsViewMemPool viewMemPool(pcoinsTip.get(), pool);
|
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");
|
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||||
|
|
||||||
CAmount nFees = 0;
|
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));
|
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(),
|
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, chainActive.Height(),
|
||||||
fSpendsCoinbase, nSigOpsCost, lp);
|
fSpendsCoinbase, nSigOpsCost, lp, setPeginsSpent);
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
|
||||||
// Check that the transaction doesn't have an excessive number of
|
// 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
|
// need to turn both off, and compare against just turning off CLEANSTACK
|
||||||
// to see if the failure is specifically due to witness validation.
|
// to see if the failure is specifically due to witness validation.
|
||||||
CValidationState stateDummy; // Want reported failures to be from first CheckInputs
|
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) &&
|
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)) {
|
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.
|
// Only the witness is missing, so the transaction itself may be fine.
|
||||||
state.SetCorruptionPossible();
|
state.SetCorruptionPossible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false; // state filled in by CheckInputs
|
return false; // state filled in by CheckInputs
|
||||||
}
|
}
|
||||||
|
|
@ -1352,9 +1358,16 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
||||||
if (!tx.IsCoinBase()) {
|
if (!tx.IsCoinBase()) {
|
||||||
txundo.vprevout.reserve(tx.vin.size());
|
txundo.vprevout.reserve(tx.vin.size());
|
||||||
for (const CTxIn &txin : tx.vin) {
|
for (const CTxIn &txin : tx.vin) {
|
||||||
txundo.vprevout.emplace_back();
|
if (txin.m_is_pegin) {
|
||||||
bool is_spent = inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
|
std::pair<uint256, COutPoint> outpoint = std::make_pair(uint256(txin.m_pegin_witness.stack[2]), txin.prevout);
|
||||||
assert(is_spent);
|
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
|
// 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++) {
|
for (unsigned int i = 0; i < tx.vin.size(); i++) {
|
||||||
const COutPoint &prevout = tx.vin[i].prevout;
|
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());
|
assert(!coin.IsSpent());
|
||||||
|
|
||||||
// We very carefully only pass in things to CScriptCheck which
|
// 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.
|
// non-upgraded nodes.
|
||||||
CScriptCheck check2(coin.out, tx, i,
|
CScriptCheck check2(coin.out, tx, i,
|
||||||
flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata);
|
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())));
|
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
|
// Failures of other flags indicate a transaction that is
|
||||||
// invalid in new blocks, e.g. an invalid P2SH. We DoS ban
|
// 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.
|
* @param out The out point that corresponds to the tx input.
|
||||||
* @return A DisconnectResult as an int
|
* @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;
|
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) {
|
if (undo.nHeight == 0) {
|
||||||
// Missing undo metadata (height and coinbase). Older versions included this
|
// Missing undo metadata (height and coinbase). Older versions included this
|
||||||
// information only in undo records for the last spend of a transactions'
|
// 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.
|
// outputs. This implies that it must be present for some other output of the same tx.
|
||||||
const Coin& alternate = AccessByTxid(view, out.hash);
|
const Coin& alternate = AccessByTxid(view, out.hash);
|
||||||
if (!alternate.IsSpent()) {
|
if (!alternate.IsSpent()) {
|
||||||
undo.nHeight = alternate.nHeight;
|
undo.nHeight = alternate.nHeight;
|
||||||
undo.fCoinBase = alternate.fCoinBase;
|
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 {
|
} 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;
|
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;) {
|
for (unsigned int j = tx.vin.size(); j-- > 0;) {
|
||||||
const COutPoint &out = tx.vin[j].prevout;
|
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;
|
if (res == DISCONNECT_FAILED) return DISCONNECT_FAILED;
|
||||||
fClean = fClean && res != DISCONNECT_UNCLEAN;
|
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()
|
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
||||||
* can fail if those validity checks fail (among other reasons). */
|
* can fail if those validity checks fail (among other reasons). */
|
||||||
bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex,
|
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);
|
AssertLockHeld(cs_main);
|
||||||
assert(pindex);
|
assert(pindex);
|
||||||
|
|
@ -2037,6 +2075,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
||||||
std::vector<PrecomputedTransactionData> txdata;
|
std::vector<PrecomputedTransactionData> txdata;
|
||||||
txdata.reserve(block.vtx.size()); // Required so that pointers to individual PrecomputedTransactionData don't get invalidated
|
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++)
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
{
|
{
|
||||||
const CTransaction &tx = *(block.vtx[i]);
|
const CTransaction &tx = *(block.vtx[i]);
|
||||||
|
|
@ -2046,7 +2089,8 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl
|
||||||
if (!tx.IsCoinBase())
|
if (!tx.IsCoinBase())
|
||||||
{
|
{
|
||||||
CAmount txfee = 0;
|
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));
|
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state));
|
||||||
}
|
}
|
||||||
nFees += txfee;
|
nFees += txfee;
|
||||||
|
|
@ -2493,13 +2537,42 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
|
||||||
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
|
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
|
||||||
int64_t nTime3;
|
int64_t nTime3;
|
||||||
LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO);
|
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());
|
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);
|
GetMainSignals().BlockChecked(blockConnecting, state);
|
||||||
if (!rv) {
|
if (!rv) {
|
||||||
if (state.IsInvalid())
|
if (state.IsInvalid()) {
|
||||||
InvalidBlockFound(pindexNew, state);
|
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());
|
return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString());
|
||||||
}
|
}
|
||||||
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
|
nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2;
|
||||||
|
|
@ -2515,7 +2588,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
|
||||||
int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4;
|
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);
|
LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal);
|
||||||
// Remove conflicting transactions from the mempool.;
|
// Remove conflicting transactions from the mempool.;
|
||||||
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight);
|
mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight, setPeginsSpent);
|
||||||
disconnectpool.removeForBlock(blockConnecting.vtx);
|
disconnectpool.removeForBlock(blockConnecting.vtx);
|
||||||
// Update chainActive & related variables.
|
// Update chainActive & related variables.
|
||||||
chainActive.SetTip(pindexNew);
|
chainActive.SetTip(pindexNew);
|
||||||
|
|
@ -3642,7 +3715,7 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams,
|
||||||
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
|
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state));
|
||||||
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
|
if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev))
|
||||||
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state));
|
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;
|
return false;
|
||||||
assert(state.IsValid());
|
assert(state.IsValid());
|
||||||
|
|
||||||
|
|
@ -4112,7 +4185,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview,
|
||||||
CBlock block;
|
CBlock block;
|
||||||
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
|
if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus()))
|
||||||
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString());
|
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));
|
return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3028,7 +3028,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac
|
||||||
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
|
||||||
// Lastly, ensure this tx will pass the mempool's chain limits
|
// Lastly, ensure this tx will pass the mempool's chain limits
|
||||||
LockPoints lp;
|
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;
|
CTxMemPool::setEntries setAncestors;
|
||||||
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
|
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
|
||||||
size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
|
size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue