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; 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

View file

@ -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

View file

@ -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) */

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_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 {

View file

@ -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;

View file

@ -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

View file

@ -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));

View file

@ -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);
} }
/** /**

View file

@ -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();

View file

@ -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());

View file

@ -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

View file

@ -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);
} }

View file

@ -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

View file

@ -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));
} }
} }

View file

@ -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;