Skip to content

Commit

Permalink
Prevent pegin double spends from entering the mempool
Browse files Browse the repository at this point in the history
This bug got introduced with the 0.17 rebase. In 0.14.1 the double pegin
claim check was done in the beginning of the validation logic, where the
coins view was the actual UTXO set. In 0.17, Core moved several checks
because the intermediate method CheckInputs was removed and some logic
moved to tx_verify.cpp, where we also put the pegin check.  In the
mempool acceptance check, however (as opposed to the ConnectBlock
check), CheckTxInputs is called with only a partial UTXO view for
optimization reasons. This commit adds an extra pegin check in the
beginning of the mempool code where we have a full UTXO view.
  • Loading branch information
stevenroose committed May 8, 2019
1 parent 50c5570 commit af22646
Showing 1 changed file with 19 additions and 2 deletions.
21 changes: 19 additions & 2 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -681,10 +681,27 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
}

// do all inputs exist?
for (const CTxIn& txin : tx.vin) {
for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxIn& txin = tx.vin[i];

// ELEMENTS:
// Don't look for coins that only exist in parent chain
// For pegin inputs check whether the pegins have already been claimed before.
// This only checks the UTXO set for already claimed pegins. For mempool conflicts,
// we rely on the GetConflictTx check done above.
if (txin.m_is_pegin) {
// Quick sanity check on witness first.
if (tx.witness.vtxinwit.size() <= i ||
tx.witness.vtxinwit[i].m_pegin_witness.stack.size() < 6 ||
uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]).IsNull() ||
tx.vin[i].prevout.hash.IsNull()) {
return state.Invalid(false, REJECT_INVALID, "pegin-no-witness");
}

std::pair<uint256, COutPoint> pegin = std::make_pair(uint256(tx.witness.vtxinwit[i].m_pegin_witness.stack[2]), tx.vin[i].prevout);
// This assumes non-null prevout and genesis block hash
if (view.IsPeginSpent(pegin)) {
return state.Invalid(false, REJECT_INVALID, "pegin-already-claimed");
}
continue;
}

Expand Down

0 comments on commit af22646

Please sign in to comment.