/** Apply the effects of a block on the utxo cache, ignoring that it may already have been applied. */ bool CViewManager::ConnectBlock(const CBlock &block, const CBlockIndex *pIndex, CCoinsViewCache &viewCache) { for (const CTransactionRef &tx : block.vtx) { if (!tx->IsCoinBase()) { for (const CTxIn &txin : tx->vin) { viewCache.SpendCoin(txin.prevout); } } // Pass check = true as every addition may be an overwrite. AddCoins(viewCache, *tx, pIndex->nHeight, true); } return true; }
void CViewManager::UpdateCoins(const CTransaction &tx, CCoinsViewCache &inputs, CTxUndo &txundo, int nHeight) { // mark inputs spent if (!tx.IsCoinBase()) { txundo.vprevout.reserve(tx.vin.size()); for (const CTxIn &txin : tx.vin) { txundo.vprevout.emplace_back(); bool is_spent = inputs.SpendCoin(txin.prevout, &txundo.vprevout.back()); assert(is_spent); } } // add outputs AddCoins(inputs, tx, nHeight); }
/** Undo the effects of this block (with given index) on the UTXO set represented by coins. * When FAILED is returned, view is left in an indeterminate state. */ DisconnectResult CViewManager::DisconnectBlock(const CBlock &block, const CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) { if (pfClean) *pfClean = false; bool fClean = true; CBlockUndo blockUndo; CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) { ELogFormat("no undo data available"); return DISCONNECT_FAILED; } if (!UndoReadFromDisk(blockUndo, pos, pindex->pprev->GetBlockHash())) { ELogFormat("failure reading undo data"); return DISCONNECT_FAILED; } if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) { ELogFormat("block and undo data inconsistent"); return DISCONNECT_FAILED; } // undo transactions in reverse order for (int i = block.vtx.size() - 1; i >= 0; i--) { const CTransaction &tx = *(block.vtx[i]); uint256 hash = tx.GetHash(); bool is_coinbase = tx.IsCoinBase(); // Check that all outputs are available and match the outputs in the block itself // exactly. for (size_t o = 0; o < tx.vout.size(); o++) { if (!tx.vout[o].scriptPubKey.IsUnspendable()) { COutPoint out(hash, o); Coin coin; bool is_spent = view.SpendCoin(out, &coin); if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) { fClean = false; // transaction output mismatch } } } // restore inputs if (i > 0) { // not coinbases if(tx.IsCoinBase2()){ continue; //continue } CTxUndo &txundo = blockUndo.vtxundo[i - 1]; if (txundo.vprevout.size() != tx.vin.size()) { ELogFormat("transaction and undo data inconsistent"); return DISCONNECT_FAILED; } for (unsigned int j = tx.vin.size(); j-- > 0;) { const COutPoint &out = tx.vin[j].prevout; int res = ApplyTxInUndo(std::move(txundo.vprevout[j]), view, out); if (res == DISCONNECT_FAILED) return DISCONNECT_FAILED; fClean = fClean && res != DISCONNECT_UNCLEAN; } // At this point, all of txundo.vprevout should have been moved out. } } // move best block pointer to prevout block view.SetBestBlock(pindex->pprev->GetBlockHash()); //sbtc-vm GET_CONTRACT_INTERFACE(ifContractObj); uint256 hashStateRoot; uint256 hashUTXORoot; CBlock prevblock; if (!ReadBlockFromDisk(prevblock, pindex->pprev, Params().GetConsensus())) { //TODO LogError rLogError("ReadBlockFromDisk failed at %d, hash=%s", pindex->pprev->nHeight, pindex->pprev->GetBlockHash().ToString()); } else { if(prevblock.GetVMState(hashStateRoot, hashUTXORoot) == RET_VM_STATE_ERR) { ILogFormat("GetVMState err"); } } ifContractObj->UpdateState(hashStateRoot, hashUTXORoot); // ifContractObj->UpdateState(pindex->pprev->hashStateRoot, pindex->pprev->hashUTXORoot); GET_CHAIN_INTERFACE(ifChainObj); if (pfClean == NULL && ifChainObj->IsLogEvents()) { ifContractObj->DeleteResults(block.vtx); ifChainObj->GetBlockTreeDB()->EraseHeightIndex(pindex->nHeight); } return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; }