mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 20:42:59 +01:00
Switch from per-tx to per-txout CCoinsViewCache methods in some places
This commit is contained in:
parent
0003911326
commit
f68cdfe92b
@ -35,14 +35,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
||||
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
|
||||
dummyTransactions[0].vout[1].nValue = 50 * CENT;
|
||||
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
|
||||
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
|
||||
AddCoins(coinsRet, dummyTransactions[0], 0);
|
||||
|
||||
dummyTransactions[1].vout.resize(2);
|
||||
dummyTransactions[1].vout[0].nValue = 21 * CENT;
|
||||
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
|
||||
dummyTransactions[1].vout[1].nValue = 22 * CENT;
|
||||
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
|
||||
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
|
||||
AddCoins(coinsRet, dummyTransactions[1], 0);
|
||||
|
||||
return dummyTransactions;
|
||||
}
|
||||
|
@ -556,24 +556,26 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
||||
if (nOut < 0)
|
||||
throw std::runtime_error("vout must be positive");
|
||||
|
||||
COutPoint out(txid, nOut);
|
||||
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||
const Coin& coin = view.AccessCoin(out);
|
||||
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
|
||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw std::runtime_error(err);
|
||||
}
|
||||
if ((unsigned int)nOut >= coins->vout.size())
|
||||
coins->vout.resize(nOut+1);
|
||||
coins->vout[nOut].scriptPubKey = scriptPubKey;
|
||||
coins->vout[nOut].nValue = 0;
|
||||
Coin newcoin;
|
||||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = 0;
|
||||
if (prevOut.exists("amount")) {
|
||||
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
|
||||
newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(out, std::move(newcoin), true);
|
||||
}
|
||||
|
||||
// if redeemScript given and private keys given,
|
||||
@ -595,13 +597,13 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||
CTxIn& txin = mergedTx.vin[i];
|
||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (!coins || !coins->IsAvailable(txin.prevout.n)) {
|
||||
const Coin& coin = view.AccessCoin(txin.prevout);
|
||||
if (coin.IsPruned()) {
|
||||
fComplete = false;
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
|
||||
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
|
||||
const CScript& prevPubKey = coin.out.scriptPubKey;
|
||||
const CAmount& amount = coin.out.nValue;
|
||||
|
||||
SignatureData sigdata;
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
|
@ -213,20 +213,20 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
const COutPoint &prevout = tx.vin[i].prevout;
|
||||
const CCoins *coins = inputs.AccessCoins(prevout.hash);
|
||||
assert(coins);
|
||||
const Coin& coin = inputs.AccessCoin(prevout);
|
||||
assert(!coin.IsPruned());
|
||||
|
||||
// If prev is coinbase, check that it's matured
|
||||
if (coins->IsCoinBase()) {
|
||||
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY)
|
||||
if (coin.IsCoinBase()) {
|
||||
if (nSpendHeight - coin.nHeight < COINBASE_MATURITY)
|
||||
return state.Invalid(false,
|
||||
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight));
|
||||
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
||||
}
|
||||
|
||||
// Check for negative or overflow input values
|
||||
nValueIn += coins->vout[prevout.n].nValue;
|
||||
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
|
||||
nValueIn += coin.out.nValue;
|
||||
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn))
|
||||
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
|
||||
|
||||
}
|
||||
|
@ -637,9 +637,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
||||
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
|
||||
|
||||
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
|
||||
const uint256& prevHash = txin.prevout.hash;
|
||||
CCoins coins;
|
||||
view.AccessCoins(prevHash); // this is certainly allowed to fail
|
||||
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
|
||||
}
|
||||
|
||||
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
|
||||
@ -691,24 +689,26 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
||||
if (nOut < 0)
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
||||
|
||||
COutPoint out(txid, nOut);
|
||||
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||
|
||||
{
|
||||
CCoinsModifier coins = view.ModifyCoins(txid);
|
||||
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
|
||||
const Coin& coin = view.AccessCoin(out);
|
||||
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
|
||||
std::string err("Previous output scriptPubKey mismatch:\n");
|
||||
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
|
||||
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
|
||||
ScriptToAsmStr(scriptPubKey);
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
||||
}
|
||||
if ((unsigned int)nOut >= coins->vout.size())
|
||||
coins->vout.resize(nOut+1);
|
||||
coins->vout[nOut].scriptPubKey = scriptPubKey;
|
||||
coins->vout[nOut].nValue = 0;
|
||||
Coin newcoin;
|
||||
newcoin.out.scriptPubKey = scriptPubKey;
|
||||
newcoin.out.nValue = 0;
|
||||
if (prevOut.exists("amount")) {
|
||||
coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
|
||||
}
|
||||
newcoin.nHeight = 1;
|
||||
view.AddCoin(out, std::move(newcoin), true);
|
||||
}
|
||||
|
||||
// if redeemScript given and not using the local wallet (private keys
|
||||
@ -766,13 +766,13 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
|
||||
// Sign what we can:
|
||||
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
|
||||
CTxIn& txin = mergedTx.vin[i];
|
||||
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
|
||||
const Coin& coin = view.AccessCoin(txin.prevout);
|
||||
if (coin.IsPruned()) {
|
||||
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
|
||||
continue;
|
||||
}
|
||||
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
|
||||
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
|
||||
const CScript& prevPubKey = coin.out.scriptPubKey;
|
||||
const CAmount& amount = coin.out.nValue;
|
||||
|
||||
SignatureData sigdata;
|
||||
// Only sign SIGHASH_SINGLE if there's a corresponding output:
|
||||
@ -844,9 +844,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
nMaxRawTxFee = 0;
|
||||
|
||||
CCoinsViewCache &view = *pcoinsTip;
|
||||
const CCoins* existingCoins = view.AccessCoins(hashTx);
|
||||
bool fHaveChain = false;
|
||||
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
|
||||
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
|
||||
fHaveChain = !existingCoin.IsPruned();
|
||||
}
|
||||
bool fHaveMempool = mempool.exists(hashTx);
|
||||
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
|
||||
if (!fHaveMempool && !fHaveChain) {
|
||||
// push to local node and sync with wallets
|
||||
CValidationState state;
|
||||
|
@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
|
||||
txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
|
||||
txFrom.vout[6].nValue = 6000;
|
||||
|
||||
coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0);
|
||||
AddCoins(coins, txFrom, 0);
|
||||
|
||||
CMutableTransaction txTo;
|
||||
txTo.vout.resize(1);
|
||||
|
@ -102,7 +102,7 @@ void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableT
|
||||
spendingTx.vout[0].nValue = 1;
|
||||
spendingTx.vout[0].scriptPubKey = CScript();
|
||||
|
||||
coins.ModifyCoins(creationTx.GetHash())->FromTx(creationTx, 0);
|
||||
AddCoins(coins, creationTx, 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(GetTxSigOpCost)
|
||||
|
@ -307,14 +307,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
|
||||
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
|
||||
dummyTransactions[0].vout[1].nValue = 50*CENT;
|
||||
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
|
||||
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
|
||||
AddCoins(coinsRet, dummyTransactions[0], 0);
|
||||
|
||||
dummyTransactions[1].vout.resize(2);
|
||||
dummyTransactions[1].vout[0].nValue = 21*CENT;
|
||||
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
|
||||
dummyTransactions[1].vout[1].nValue = 22*CENT;
|
||||
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
|
||||
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
|
||||
AddCoins(coinsRet, dummyTransactions[1], 0);
|
||||
|
||||
return dummyTransactions;
|
||||
}
|
||||
|
@ -498,8 +498,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
// during reorgs to ensure COINBASE_MATURITY is still met.
|
||||
bool fSpendsCoinbase = false;
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
|
||||
if (coins->IsCoinBase()) {
|
||||
const Coin &coin = view.AccessCoin(txin.prevout);
|
||||
if (coin.IsCoinBase()) {
|
||||
fSpendsCoinbase = true;
|
||||
break;
|
||||
}
|
||||
@ -818,15 +818,8 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
|
||||
}
|
||||
|
||||
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
|
||||
int nHeight = -1;
|
||||
{
|
||||
const CCoinsViewCache& view = *pcoinsTip;
|
||||
const CCoins* coins = view.AccessCoins(hash);
|
||||
if (coins)
|
||||
nHeight = coins->nHeight;
|
||||
}
|
||||
if (nHeight > 0)
|
||||
pindexSlow = chainActive[nHeight];
|
||||
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
|
||||
if (!coin.IsPruned()) pindexSlow = chainActive[coin.nHeight];
|
||||
}
|
||||
|
||||
if (pindexSlow) {
|
||||
@ -1074,19 +1067,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
||||
if (!tx.IsCoinBase()) {
|
||||
txundo.vprevout.reserve(tx.vin.size());
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
|
||||
CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash);
|
||||
unsigned nPos = txin.prevout.n;
|
||||
|
||||
if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull())
|
||||
assert(false);
|
||||
// mark an outpoint spent, and construct undo information
|
||||
txundo.vprevout.emplace_back(coins->vout[nPos], coins->nHeight, coins->fCoinBase);
|
||||
bool ret = coins->Spend(nPos);
|
||||
assert(ret);
|
||||
txundo.vprevout.emplace_back();
|
||||
inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
|
||||
}
|
||||
}
|
||||
// add outputs
|
||||
inputs.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight);
|
||||
AddCoins(inputs, tx, nHeight);
|
||||
}
|
||||
|
||||
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
|
||||
@ -1260,24 +1246,21 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
||||
{
|
||||
bool fClean = true;
|
||||
|
||||
CCoinsModifier coins = view.ModifyCoins(out.hash);
|
||||
if (undo.nHeight != 0) {
|
||||
if (!coins->IsPruned()) {
|
||||
if (coins->fCoinBase != undo.fCoinBase || (uint32_t)coins->nHeight != undo.nHeight) fClean = false; // metadata mismatch
|
||||
if (view.HaveCoins(out)) fClean = false; // overwriting transaction output
|
||||
|
||||
if (undo.nHeight == 0) {
|
||||
// Missing undo metadata (height and coinbase). Older versions included this
|
||||
// information only in undo records for the last spend of a transactions'
|
||||
// outputs. This implies that it must be present for some other output of the same tx.
|
||||
const Coin& alternate = AccessByTxid(view, out.hash);
|
||||
if (!alternate.IsPruned()) {
|
||||
undo.nHeight = alternate.nHeight;
|
||||
undo.fCoinBase = alternate.fCoinBase;
|
||||
} else {
|
||||
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
||||
}
|
||||
// restore height/coinbase tx metadata from undo data
|
||||
coins->fCoinBase = undo.fCoinBase;
|
||||
coins->nHeight = undo.nHeight;
|
||||
} else {
|
||||
// Undo data does not contain height/coinbase. This should never happen
|
||||
// for newly created undo entries. Previously, this data was only saved
|
||||
// for the last spend of a transaction's outputs, so check IsPruned().
|
||||
if (coins->IsPruned()) fClean = false; // adding output to missing transaction
|
||||
}
|
||||
if (coins->IsAvailable(out.n)) fClean = false; // overwriting existing output
|
||||
if (coins->vout.size() < out.n+1)
|
||||
coins->vout.resize(out.n+1);
|
||||
coins->vout[out.n] = std::move(undo.out);
|
||||
view.AddCoin(out, std::move(undo), undo.fCoinBase);
|
||||
|
||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||
}
|
||||
@ -1313,15 +1296,15 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
|
||||
|
||||
// Check that all outputs are available and match the outputs in the block itself
|
||||
// exactly.
|
||||
{
|
||||
CCoinsModifier outs = view.ModifyCoins(hash);
|
||||
outs->ClearUnspendable();
|
||||
|
||||
CCoins outsBlock(tx, pindex->nHeight);
|
||||
if (*outs != outsBlock) fClean = false; // transaction mismatch
|
||||
|
||||
// remove outputs
|
||||
outs->Clear();
|
||||
for (size_t o = 0; o < tx.vout.size(); o++) {
|
||||
if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
|
||||
COutPoint out(hash, o);
|
||||
Coin coin;
|
||||
view.SpendCoin(out, &coin);
|
||||
if (tx.vout[o] != coin.out) {
|
||||
fClean = false; // transaction output mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// restore inputs
|
||||
@ -1518,10 +1501,12 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
|
||||
if (fEnforceBIP30) {
|
||||
for (const auto& tx : block.vtx) {
|
||||
const CCoins* coins = view.AccessCoins(tx->GetHash());
|
||||
if (coins && !coins->IsPruned())
|
||||
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
||||
REJECT_INVALID, "bad-txns-BIP30");
|
||||
for (size_t o = 0; o < tx->vout.size(); o++) {
|
||||
if (view.HaveCoins(COutPoint(tx->GetHash(), o))) {
|
||||
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
|
||||
REJECT_INVALID, "bad-txns-BIP30");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1588,7 +1573,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
|
||||
// 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.AccessCoins(tx.vin[j].prevout.hash)->nHeight;
|
||||
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
|
||||
}
|
||||
|
||||
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
|
||||
|
Loading…
Reference in New Issue
Block a user