Browse Source

Switch from per-tx to per-txout CCoinsViewCache methods in some places

0.15
Pieter Wuille 8 years ago
parent
commit
f68cdfe92b
  1. 4
      src/bench/ccoins_caching.cpp
  2. 26
      src/bitcoin-tx.cpp
  3. 14
      src/consensus/tx_verify.cpp
  4. 37
      src/rpc/rawtransaction.cpp
  5. 2
      src/test/script_P2SH_tests.cpp
  6. 2
      src/test/sigopcount_tests.cpp
  7. 4
      src/test/transaction_tests.cpp
  8. 87
      src/validation.cpp

4
src/bench/ccoins_caching.cpp

@ -35,14 +35,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) @@ -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;
}

26
src/bitcoin-tx.cpp

@ -556,24 +556,26 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) @@ -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) @@ -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:

14
src/consensus/tx_verify.cpp

@ -213,20 +213,20 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, c @@ -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");
}

37
src/rpc/rawtransaction.cpp

@ -637,9 +637,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) @@ -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) @@ -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) @@ -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) @@ -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;

2
src/test/script_P2SH_tests.cpp

@ -316,7 +316,7 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard) @@ -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);

2
src/test/sigopcount_tests.cpp

@ -102,7 +102,7 @@ void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableT @@ -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)

4
src/test/transaction_tests.cpp

@ -307,14 +307,14 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet) @@ -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;
}

87
src/validation.cpp

@ -498,8 +498,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C @@ -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 @@ -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 @@ -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) @@ -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* @@ -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 @@ -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 @@ -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…
Cancel
Save