Skip to content

Commit f68cdfe

Browse files
committed
Switch from per-tx to per-txout CCoinsViewCache methods in some places
1 parent 0003911 commit f68cdfe

File tree

8 files changed

+83
-93
lines changed

8 files changed

+83
-93
lines changed

src/bench/ccoins_caching.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
3535
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
3636
dummyTransactions[0].vout[1].nValue = 50 * CENT;
3737
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
38-
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
38+
AddCoins(coinsRet, dummyTransactions[0], 0);
3939

4040
dummyTransactions[1].vout.resize(2);
4141
dummyTransactions[1].vout[0].nValue = 21 * CENT;
4242
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
4343
dummyTransactions[1].vout[1].nValue = 22 * CENT;
4444
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
45-
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
45+
AddCoins(coinsRet, dummyTransactions[1], 0);
4646

4747
return dummyTransactions;
4848
}

src/bitcoin-tx.cpp

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -556,24 +556,26 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
556556
if (nOut < 0)
557557
throw std::runtime_error("vout must be positive");
558558

559+
COutPoint out(txid, nOut);
559560
std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
560561
CScript scriptPubKey(pkData.begin(), pkData.end());
561562

562563
{
563-
CCoinsModifier coins = view.ModifyCoins(txid);
564-
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
564+
const Coin& coin = view.AccessCoin(out);
565+
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
565566
std::string err("Previous output scriptPubKey mismatch:\n");
566-
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
567+
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
567568
ScriptToAsmStr(scriptPubKey);
568569
throw std::runtime_error(err);
569570
}
570-
if ((unsigned int)nOut >= coins->vout.size())
571-
coins->vout.resize(nOut+1);
572-
coins->vout[nOut].scriptPubKey = scriptPubKey;
573-
coins->vout[nOut].nValue = 0;
571+
Coin newcoin;
572+
newcoin.out.scriptPubKey = scriptPubKey;
573+
newcoin.out.nValue = 0;
574574
if (prevOut.exists("amount")) {
575-
coins->vout[nOut].nValue = AmountFromValue(prevOut["amount"]);
575+
newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
576576
}
577+
newcoin.nHeight = 1;
578+
view.AddCoin(out, std::move(newcoin), true);
577579
}
578580

579581
// if redeemScript given and private keys given,
@@ -595,13 +597,13 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
595597
// Sign what we can:
596598
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
597599
CTxIn& txin = mergedTx.vin[i];
598-
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
599-
if (!coins || !coins->IsAvailable(txin.prevout.n)) {
600+
const Coin& coin = view.AccessCoin(txin.prevout);
601+
if (coin.IsPruned()) {
600602
fComplete = false;
601603
continue;
602604
}
603-
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
604-
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
605+
const CScript& prevPubKey = coin.out.scriptPubKey;
606+
const CAmount& amount = coin.out.nValue;
605607

606608
SignatureData sigdata;
607609
// Only sign SIGHASH_SINGLE if there's a corresponding output:

src/consensus/tx_verify.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,20 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c
213213
for (unsigned int i = 0; i < tx.vin.size(); i++)
214214
{
215215
const COutPoint &prevout = tx.vin[i].prevout;
216-
const CCoins *coins = inputs.AccessCoins(prevout.hash);
217-
assert(coins);
216+
const Coin& coin = inputs.AccessCoin(prevout);
217+
assert(!coin.IsPruned());
218218

219219
// If prev is coinbase, check that it's matured
220-
if (coins->IsCoinBase()) {
221-
if (nSpendHeight - coins->nHeight < COINBASE_MATURITY)
220+
if (coin.IsCoinBase()) {
221+
if (nSpendHeight - coin.nHeight < COINBASE_MATURITY)
222222
return state.Invalid(false,
223223
REJECT_INVALID, "bad-txns-premature-spend-of-coinbase",
224-
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coins->nHeight));
224+
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
225225
}
226226

227227
// Check for negative or overflow input values
228-
nValueIn += coins->vout[prevout.n].nValue;
229-
if (!MoneyRange(coins->vout[prevout.n].nValue) || !MoneyRange(nValueIn))
228+
nValueIn += coin.out.nValue;
229+
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn))
230230
return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange");
231231

232232
}

src/rpc/rawtransaction.cpp

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -637,9 +637,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
637637
view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
638638

639639
BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
640-
const uint256& prevHash = txin.prevout.hash;
641-
CCoins coins;
642-
view.AccessCoins(prevHash); // this is certainly allowed to fail
640+
view.AccessCoin(txin.prevout); // Load entries from viewChain into view; can fail.
643641
}
644642

645643
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
@@ -691,24 +689,26 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
691689
if (nOut < 0)
692690
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
693691

692+
COutPoint out(txid, nOut);
694693
std::vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
695694
CScript scriptPubKey(pkData.begin(), pkData.end());
696695

697696
{
698-
CCoinsModifier coins = view.ModifyCoins(txid);
699-
if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
697+
const Coin& coin = view.AccessCoin(out);
698+
if (!coin.IsPruned() && coin.out.scriptPubKey != scriptPubKey) {
700699
std::string err("Previous output scriptPubKey mismatch:\n");
701-
err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
700+
err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
702701
ScriptToAsmStr(scriptPubKey);
703702
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
704703
}
705-
if ((unsigned int)nOut >= coins->vout.size())
706-
coins->vout.resize(nOut+1);
707-
coins->vout[nOut].scriptPubKey = scriptPubKey;
708-
coins->vout[nOut].nValue = 0;
704+
Coin newcoin;
705+
newcoin.out.scriptPubKey = scriptPubKey;
706+
newcoin.out.nValue = 0;
709707
if (prevOut.exists("amount")) {
710-
coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount"));
708+
newcoin.out.nValue = AmountFromValue(find_value(prevOut, "amount"));
711709
}
710+
newcoin.nHeight = 1;
711+
view.AddCoin(out, std::move(newcoin), true);
712712
}
713713

714714
// if redeemScript given and not using the local wallet (private keys
@@ -766,13 +766,13 @@ UniValue signrawtransaction(const JSONRPCRequest& request)
766766
// Sign what we can:
767767
for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
768768
CTxIn& txin = mergedTx.vin[i];
769-
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
770-
if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) {
769+
const Coin& coin = view.AccessCoin(txin.prevout);
770+
if (coin.IsPruned()) {
771771
TxInErrorToJSON(txin, vErrors, "Input not found or already spent");
772772
continue;
773773
}
774-
const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
775-
const CAmount& amount = coins->vout[txin.prevout.n].nValue;
774+
const CScript& prevPubKey = coin.out.scriptPubKey;
775+
const CAmount& amount = coin.out.nValue;
776776

777777
SignatureData sigdata;
778778
// Only sign SIGHASH_SINGLE if there's a corresponding output:
@@ -844,9 +844,12 @@ UniValue sendrawtransaction(const JSONRPCRequest& request)
844844
nMaxRawTxFee = 0;
845845

846846
CCoinsViewCache &view = *pcoinsTip;
847-
const CCoins* existingCoins = view.AccessCoins(hashTx);
847+
bool fHaveChain = false;
848+
for (size_t o = 0; !fHaveChain && o < tx->vout.size(); o++) {
849+
const Coin& existingCoin = view.AccessCoin(COutPoint(hashTx, o));
850+
fHaveChain = !existingCoin.IsPruned();
851+
}
848852
bool fHaveMempool = mempool.exists(hashTx);
849-
bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000;
850853
if (!fHaveMempool && !fHaveChain) {
851854
// push to local node and sync with wallets
852855
CValidationState state;

src/test/script_P2SH_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
316316
txFrom.vout[6].scriptPubKey = GetScriptForDestination(CScriptID(twentySigops));
317317
txFrom.vout[6].nValue = 6000;
318318

319-
coins.ModifyCoins(txFrom.GetHash())->FromTx(txFrom, 0);
319+
AddCoins(coins, txFrom, 0);
320320

321321
CMutableTransaction txTo;
322322
txTo.vout.resize(1);

src/test/sigopcount_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableT
102102
spendingTx.vout[0].nValue = 1;
103103
spendingTx.vout[0].scriptPubKey = CScript();
104104

105-
coins.ModifyCoins(creationTx.GetHash())->FromTx(creationTx, 0);
105+
AddCoins(coins, creationTx, 0);
106106
}
107107

108108
BOOST_AUTO_TEST_CASE(GetTxSigOpCost)

src/test/transaction_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,14 +307,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
307307
dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
308308
dummyTransactions[0].vout[1].nValue = 50*CENT;
309309
dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
310-
coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
310+
AddCoins(coinsRet, dummyTransactions[0], 0);
311311

312312
dummyTransactions[1].vout.resize(2);
313313
dummyTransactions[1].vout[0].nValue = 21*CENT;
314314
dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
315315
dummyTransactions[1].vout[1].nValue = 22*CENT;
316316
dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
317-
coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
317+
AddCoins(coinsRet, dummyTransactions[1], 0);
318318

319319
return dummyTransactions;
320320
}

src/validation.cpp

Lines changed: 36 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
498498
// during reorgs to ensure COINBASE_MATURITY is still met.
499499
bool fSpendsCoinbase = false;
500500
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
501-
const CCoins *coins = view.AccessCoins(txin.prevout.hash);
502-
if (coins->IsCoinBase()) {
501+
const Coin &coin = view.AccessCoin(txin.prevout);
502+
if (coin.IsCoinBase()) {
503503
fSpendsCoinbase = true;
504504
break;
505505
}
@@ -818,15 +818,8 @@ bool GetTransaction(const uint256 &hash, CTransactionRef &txOut, const Consensus
818818
}
819819

820820
if (fAllowSlow) { // use coin database to locate block that contains transaction, and scan it
821-
int nHeight = -1;
822-
{
823-
const CCoinsViewCache& view = *pcoinsTip;
824-
const CCoins* coins = view.AccessCoins(hash);
825-
if (coins)
826-
nHeight = coins->nHeight;
827-
}
828-
if (nHeight > 0)
829-
pindexSlow = chainActive[nHeight];
821+
const Coin& coin = AccessByTxid(*pcoinsTip, hash);
822+
if (!coin.IsPruned()) pindexSlow = chainActive[coin.nHeight];
830823
}
831824

832825
if (pindexSlow) {
@@ -1074,19 +1067,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
10741067
if (!tx.IsCoinBase()) {
10751068
txundo.vprevout.reserve(tx.vin.size());
10761069
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
1077-
CCoinsModifier coins = inputs.ModifyCoins(txin.prevout.hash);
1078-
unsigned nPos = txin.prevout.n;
1079-
1080-
if (nPos >= coins->vout.size() || coins->vout[nPos].IsNull())
1081-
assert(false);
1082-
// mark an outpoint spent, and construct undo information
1083-
txundo.vprevout.emplace_back(coins->vout[nPos], coins->nHeight, coins->fCoinBase);
1084-
bool ret = coins->Spend(nPos);
1085-
assert(ret);
1070+
txundo.vprevout.emplace_back();
1071+
inputs.SpendCoin(txin.prevout, &txundo.vprevout.back());
10861072
}
10871073
}
10881074
// add outputs
1089-
inputs.ModifyNewCoins(tx.GetHash(), tx.IsCoinBase())->FromTx(tx, nHeight);
1075+
AddCoins(inputs, tx, nHeight);
10901076
}
10911077

10921078
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight)
@@ -1260,24 +1246,21 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
12601246
{
12611247
bool fClean = true;
12621248

1263-
CCoinsModifier coins = view.ModifyCoins(out.hash);
1264-
if (undo.nHeight != 0) {
1265-
if (!coins->IsPruned()) {
1266-
if (coins->fCoinBase != undo.fCoinBase || (uint32_t)coins->nHeight != undo.nHeight) fClean = false; // metadata mismatch
1249+
if (view.HaveCoins(out)) fClean = false; // overwriting transaction output
1250+
1251+
if (undo.nHeight == 0) {
1252+
// Missing undo metadata (height and coinbase). Older versions included this
1253+
// information only in undo records for the last spend of a transactions'
1254+
// outputs. This implies that it must be present for some other output of the same tx.
1255+
const Coin& alternate = AccessByTxid(view, out.hash);
1256+
if (!alternate.IsPruned()) {
1257+
undo.nHeight = alternate.nHeight;
1258+
undo.fCoinBase = alternate.fCoinBase;
1259+
} else {
1260+
return DISCONNECT_FAILED; // adding output for transaction without known metadata
12671261
}
1268-
// restore height/coinbase tx metadata from undo data
1269-
coins->fCoinBase = undo.fCoinBase;
1270-
coins->nHeight = undo.nHeight;
1271-
} else {
1272-
// Undo data does not contain height/coinbase. This should never happen
1273-
// for newly created undo entries. Previously, this data was only saved
1274-
// for the last spend of a transaction's outputs, so check IsPruned().
1275-
if (coins->IsPruned()) fClean = false; // adding output to missing transaction
12761262
}
1277-
if (coins->IsAvailable(out.n)) fClean = false; // overwriting existing output
1278-
if (coins->vout.size() < out.n+1)
1279-
coins->vout.resize(out.n+1);
1280-
coins->vout[out.n] = std::move(undo.out);
1263+
view.AddCoin(out, std::move(undo), undo.fCoinBase);
12811264

12821265
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
12831266
}
@@ -1313,15 +1296,15 @@ static DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex*
13131296

13141297
// Check that all outputs are available and match the outputs in the block itself
13151298
// exactly.
1316-
{
1317-
CCoinsModifier outs = view.ModifyCoins(hash);
1318-
outs->ClearUnspendable();
1319-
1320-
CCoins outsBlock(tx, pindex->nHeight);
1321-
if (*outs != outsBlock) fClean = false; // transaction mismatch
1322-
1323-
// remove outputs
1324-
outs->Clear();
1299+
for (size_t o = 0; o < tx.vout.size(); o++) {
1300+
if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
1301+
COutPoint out(hash, o);
1302+
Coin coin;
1303+
view.SpendCoin(out, &coin);
1304+
if (tx.vout[o] != coin.out) {
1305+
fClean = false; // transaction output mismatch
1306+
}
1307+
}
13251308
}
13261309

13271310
// restore inputs
@@ -1518,10 +1501,12 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
15181501

15191502
if (fEnforceBIP30) {
15201503
for (const auto& tx : block.vtx) {
1521-
const CCoins* coins = view.AccessCoins(tx->GetHash());
1522-
if (coins && !coins->IsPruned())
1523-
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
1524-
REJECT_INVALID, "bad-txns-BIP30");
1504+
for (size_t o = 0; o < tx->vout.size(); o++) {
1505+
if (view.HaveCoins(COutPoint(tx->GetHash(), o))) {
1506+
return state.DoS(100, error("ConnectBlock(): tried to overwrite transaction"),
1507+
REJECT_INVALID, "bad-txns-BIP30");
1508+
}
1509+
}
15251510
}
15261511
}
15271512

@@ -1588,7 +1573,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd
15881573
// be in ConnectBlock because they require the UTXO set
15891574
prevheights.resize(tx.vin.size());
15901575
for (size_t j = 0; j < tx.vin.size(); j++) {
1591-
prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight;
1576+
prevheights[j] = view.AccessCoin(tx.vin[j].prevout).nHeight;
15921577
}
15931578

15941579
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {

0 commit comments

Comments
 (0)