diff --git a/src/coins.cpp b/src/coins.cpp index da1036acfb..5da63c5202 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -236,6 +236,9 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { if (!tx.IsCoinBase()) { for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.vin[i].m_is_pegin) { + continue; + } if (!HaveCoin(tx.vin[i].prevout)) { return false; } diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index be73d0a2f9..fb07f472a0 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -54,6 +54,11 @@ std::pair CalculateSequenceLocks(const CTransaction &tx, int flags for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn& txin = tx.vin[txinIndex]; + // Peg-ins have no output height + if (txin.m_is_pegin) { + continue; + } + // Sequence numbers with the most significant bit set are not // treated as relative lock-times, nor are they given any // consensus-enforced meaning at this point. @@ -126,6 +131,11 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in unsigned int nSigOps = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { + // Peg-in inputs are segwit-only + if (tx.vin[i].m_is_pegin) { + continue; + } + const Coin& coin = inputs.AccessCoin(tx.vin[i].prevout); assert(!coin.IsSpent()); const CTxOut &prevout = coin.out; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index b8f7963a0c..d37fa4fd23 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -163,6 +163,11 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) for (unsigned int i = 0; i < tx.vin.size(); i++) { + if (tx.vin[i].m_is_pegin) { + // This deals with p2sh in general only + continue; + } + const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; std::vector > vSolutions; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 090699d86f..52eb8ca1a7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1889,6 +1889,10 @@ static UniValue getblockstats(const JSONRPCRequest& request) } CAmount tx_total_in = 0; for (const CTxIn& in : tx->vin) { + if (in.m_is_pegin) { + continue; + } + CTransactionRef tx_in; uint256 hashBlock; if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) { diff --git a/src/validation.cpp b/src/validation.cpp index 925db82061..387aafb481 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -395,6 +395,12 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { const CTxIn& txin = tx.vin[txinIndex]; + // pegins should not restrict validity of sequence locks + if (txin.m_is_pegin) { + prevheights[txinIndex] = -1; + continue; + } + Coin coin; if (!viewMemPool.GetCoin(txin.prevout, coin)) { return error("%s: Missing input", __func__); @@ -537,6 +543,10 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt assert(!tx.IsCoinBase()); for (const CTxIn& txin : tx.vin) { + if (txin.m_is_pegin) { + continue; + } + const Coin& coin = view.AccessCoin(txin.prevout); // At this point we haven't actually checked if the coins are all @@ -658,6 +668,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // do all inputs exist? for (const CTxIn& txin : tx.vin) { + // ELEMENTS: + // Don't look for coins that only exist in parent chain + if (txin.m_is_pegin) { + continue; + } + if (!pcoinsTip->HaveCoinInCache(txin.prevout)) { coins_to_uncache.push_back(txin.prevout); } @@ -714,6 +730,10 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; for (const CTxIn &txin : tx.vin) { + // ELEMENTS: + if (txin.m_is_pegin) { + continue; + } const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsCoinBase()) { fSpendsCoinbase = true; @@ -2040,7 +2060,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl // be in ConnectBlock because they require the UTXO set prevheights.resize(tx.vin.size()); for (size_t j = 0; j < tx.vin.size(); j++) { - prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight; + if (tx.vin[j].m_is_pegin) { + prevheights[j] = -1; + } else { + prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight; + } } if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5a7fdf9a85..0562f5633b 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3080,6 +3080,10 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve // Notify that old coins are spent for (const CTxIn& txin : wtxNew.tx->vin) { + // Pegins are not in our UTXO set. + if (txin.m_is_pegin) + continue; + CWalletTx &coin = mapWallet.at(txin.prevout.hash); coin.BindWallet(this); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);