Changeset View
Changeset View
Standalone View
Standalone View
src/validation.cpp
Show First 20 Lines • Show All 1,780 Lines • ▼ Show 20 Lines | DisconnectResult ApplyBlockUndo(const CBlockUndo &blockUndo, | ||||
CCoinsViewCache &view) { | CCoinsViewCache &view) { | ||||
bool fClean = true; | bool fClean = true; | ||||
if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) { | if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) { | ||||
error("DisconnectBlock(): block and undo data inconsistent"); | error("DisconnectBlock(): block and undo data inconsistent"); | ||||
return DISCONNECT_FAILED; | return DISCONNECT_FAILED; | ||||
} | } | ||||
// Undo transactions in reverse order. | // First, restore inputs. | ||||
size_t i = block.vtx.size(); | for (size_t i = 1; i < block.vtx.size(); i++) { | ||||
while (i-- > 0) { | |||||
const CTransaction &tx = *(block.vtx[i]); | const CTransaction &tx = *(block.vtx[i]); | ||||
const CTxUndo &txundo = blockUndo.vtxundo[i - 1]; | |||||
if (txundo.vprevout.size() != tx.vin.size()) { | |||||
error("DisconnectBlock(): transaction and undo data inconsistent"); | |||||
return DISCONNECT_FAILED; | |||||
} | |||||
for (size_t j = 0; j < tx.vin.size(); j++) { | |||||
const COutPoint &out = tx.vin[j].prevout; | |||||
const Coin &undo = txundo.vprevout[j]; | |||||
DisconnectResult res = UndoCoinSpend(undo, view, out); | |||||
if (res == DISCONNECT_FAILED) { | |||||
return DISCONNECT_FAILED; | |||||
} | |||||
fClean = fClean && res != DISCONNECT_UNCLEAN; | |||||
} | |||||
} | |||||
// Second, revert created outputs. | |||||
for (const auto &ptx : block.vtx) { | |||||
const CTransaction &tx = *ptx; | |||||
uint256 txid = tx.GetId(); | uint256 txid = tx.GetId(); | ||||
// Check that all outputs are available and match the outputs in the | // Check that all outputs are available and match the outputs in the | ||||
// block itself exactly. | // block itself exactly. | ||||
for (size_t o = 0; o < tx.vout.size(); o++) { | for (size_t o = 0; o < tx.vout.size(); o++) { | ||||
if (tx.vout[o].scriptPubKey.IsUnspendable()) { | if (tx.vout[o].scriptPubKey.IsUnspendable()) { | ||||
continue; | continue; | ||||
} | } | ||||
COutPoint out(txid, o); | COutPoint out(txid, o); | ||||
Coin coin; | Coin coin; | ||||
bool is_spent = view.SpendCoin(out, &coin); | bool is_spent = view.SpendCoin(out, &coin); | ||||
if (!is_spent || tx.vout[o] != coin.GetTxOut()) { | if (!is_spent || tx.vout[o] != coin.GetTxOut()) { | ||||
// transaction output mismatch | // transaction output mismatch | ||||
fClean = false; | fClean = false; | ||||
} | } | ||||
} | } | ||||
// Restore inputs. | |||||
if (i < 1) { | |||||
// Skip the coinbase. | |||||
continue; | |||||
} | |||||
const CTxUndo &txundo = blockUndo.vtxundo[i - 1]; | |||||
if (txundo.vprevout.size() != tx.vin.size()) { | |||||
error("DisconnectBlock(): transaction and undo data inconsistent"); | |||||
return DISCONNECT_FAILED; | |||||
} | |||||
for (size_t j = tx.vin.size(); j-- > 0;) { | |||||
const COutPoint &out = tx.vin[j].prevout; | |||||
const Coin &undo = txundo.vprevout[j]; | |||||
DisconnectResult res = UndoCoinSpend(undo, view, out); | |||||
if (res == DISCONNECT_FAILED) { | |||||
return DISCONNECT_FAILED; | |||||
} | |||||
fClean = fClean && res != DISCONNECT_UNCLEAN; | |||||
} | |||||
} | } | ||||
// Move best block pointer to previous block. | // Move best block pointer to previous block. | ||||
view.SetBestBlock(block.hashPrevBlock); | view.SetBestBlock(block.hashPrevBlock); | ||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 3,678 Lines • Show Last 20 Lines |