mirror of
https://github.com/twisterarmy/twister-core.git
synced 2025-01-11 07:17:53 +00:00
Make DisconnectBlock fault-tolerant
This commit is contained in:
parent
ea9788517b
commit
2cbd71da06
55
src/main.cpp
55
src/main.cpp
@ -1464,23 +1464,32 @@ bool CTransaction::ClientCheckInputs() const
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
|
bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean)
|
||||||
{
|
{
|
||||||
assert(pindex == view.GetBestBlock());
|
assert(pindex == view.GetBestBlock());
|
||||||
|
|
||||||
|
if (pfClean)
|
||||||
|
*pfClean = false;
|
||||||
|
|
||||||
|
bool fClean = true;
|
||||||
|
|
||||||
CBlockUndo blockUndo;
|
CBlockUndo blockUndo;
|
||||||
{
|
{
|
||||||
CDiskBlockPos pos = pindex->GetUndoPos();
|
CDiskBlockPos pos = pindex->GetUndoPos();
|
||||||
if (pos.IsNull())
|
if (pos.IsNull())
|
||||||
return error("DisconnectBlock() : no undo data available");
|
return error("DisconnectBlock() : no undo data available");
|
||||||
FILE *file = OpenUndoFile(pos, true);
|
CAutoFile fileUndo(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION);
|
||||||
if (file == NULL)
|
if (!fileUndo)
|
||||||
return error("DisconnectBlock() : undo file not available");
|
return error("DisconnectBlock() : cannot open undo file");
|
||||||
CAutoFile fileUndo(file, SER_DISK, CLIENT_VERSION);
|
try {
|
||||||
fileUndo >> blockUndo;
|
fileUndo >> blockUndo;
|
||||||
|
} catch(std::exception &e) {
|
||||||
|
return error("DisconnectBlock() : deserialize or I/O error reading udno data");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(blockUndo.vtxundo.size() + 1 == vtx.size());
|
if (blockUndo.vtxundo.size() + 1 != vtx.size())
|
||||||
|
return error("DisconnectBlock() : block and undo data inconsistent");
|
||||||
|
|
||||||
// undo transactions in reverse order
|
// undo transactions in reverse order
|
||||||
for (int i = vtx.size() - 1; i >= 0; i--) {
|
for (int i = vtx.size() - 1; i >= 0; i--) {
|
||||||
@ -1488,13 +1497,15 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
|
|||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
|
|
||||||
// check that all outputs are available
|
// check that all outputs are available
|
||||||
if (!view.HaveCoins(hash))
|
if (!view.HaveCoins(hash)) {
|
||||||
return error("DisconnectBlock() : outputs still spent? database corrupted");
|
fClean = fClean && error("DisconnectBlock() : outputs still spent? database corrupted");
|
||||||
|
view.SetCoins(hash, CCoins());
|
||||||
|
}
|
||||||
CCoins &outs = view.GetCoins(hash);
|
CCoins &outs = view.GetCoins(hash);
|
||||||
|
|
||||||
CCoins outsBlock = CCoins(tx, pindex->nHeight);
|
CCoins outsBlock = CCoins(tx, pindex->nHeight);
|
||||||
if (outs != outsBlock)
|
if (outs != outsBlock)
|
||||||
return error("DisconnectBlock() : added transaction mismatch? database corrupted");
|
fClean = fClean && error("DisconnectBlock() : added transaction mismatch? database corrupted");
|
||||||
|
|
||||||
// remove outputs
|
// remove outputs
|
||||||
outs = CCoins();
|
outs = CCoins();
|
||||||
@ -1502,24 +1513,27 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
|
|||||||
// restore inputs
|
// restore inputs
|
||||||
if (i > 0) { // not coinbases
|
if (i > 0) { // not coinbases
|
||||||
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
const CTxUndo &txundo = blockUndo.vtxundo[i-1];
|
||||||
assert(txundo.vprevout.size() == tx.vin.size());
|
if (txundo.vprevout.size() != tx.vin.size())
|
||||||
|
return error("DisconnectBlock() : transaction and undo data inconsistent");
|
||||||
for (unsigned int j = tx.vin.size(); j-- > 0;) {
|
for (unsigned int j = tx.vin.size(); j-- > 0;) {
|
||||||
const COutPoint &out = tx.vin[j].prevout;
|
const COutPoint &out = tx.vin[j].prevout;
|
||||||
const CTxInUndo &undo = txundo.vprevout[j];
|
const CTxInUndo &undo = txundo.vprevout[j];
|
||||||
CCoins coins;
|
CCoins coins;
|
||||||
view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent
|
view.GetCoins(out.hash, coins); // this can fail if the prevout was already entirely spent
|
||||||
if (coins.IsPruned()) {
|
if (undo.nHeight != 0) {
|
||||||
if (undo.nHeight == 0)
|
// undo data contains height: this is the last output of the prevout tx being spent
|
||||||
return error("DisconnectBlock() : undo data doesn't contain tx metadata? database corrupted");
|
if (!coins.IsPruned())
|
||||||
|
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing transaction");
|
||||||
|
coins = CCoins();
|
||||||
coins.fCoinBase = undo.fCoinBase;
|
coins.fCoinBase = undo.fCoinBase;
|
||||||
coins.nHeight = undo.nHeight;
|
coins.nHeight = undo.nHeight;
|
||||||
coins.nVersion = undo.nVersion;
|
coins.nVersion = undo.nVersion;
|
||||||
} else {
|
} else {
|
||||||
if (undo.nHeight != 0)
|
if (coins.IsPruned())
|
||||||
return error("DisconnectBlock() : undo data contains unneeded tx metadata? database corrupted");
|
fClean = fClean && error("DisconnectBlock() : undo data adding output to missing transaction");
|
||||||
}
|
}
|
||||||
if (coins.IsAvailable(out.n))
|
if (coins.IsAvailable(out.n))
|
||||||
return error("DisconnectBlock() : prevout output not spent? database corrupted");
|
fClean = fClean && error("DisconnectBlock() : undo data overwriting existing output");
|
||||||
if (coins.vout.size() < out.n+1)
|
if (coins.vout.size() < out.n+1)
|
||||||
coins.vout.resize(out.n+1);
|
coins.vout.resize(out.n+1);
|
||||||
coins.vout[out.n] = undo.txout;
|
coins.vout[out.n] = undo.txout;
|
||||||
@ -1532,7 +1546,12 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view)
|
|||||||
// move best block pointer to prevout block
|
// move best block pointer to prevout block
|
||||||
view.SetBestBlock(pindex->pprev);
|
view.SetBestBlock(pindex->pprev);
|
||||||
|
|
||||||
return true;
|
if (pfClean) {
|
||||||
|
*pfClean = fClean;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return fClean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void static FlushBlockFile()
|
void static FlushBlockFile()
|
||||||
|
@ -1305,8 +1305,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Undo the effects of this block (with given index) on the UTXO set represented by coins
|
/** Undo the effects of this block (with given index) on the UTXO set represented by coins.
|
||||||
bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins);
|
* In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean
|
||||||
|
* will be true if no problems were found. Otherwise, the return value will be false in case
|
||||||
|
* of problems. Note that in any case, coins may be modified. */
|
||||||
|
bool DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL);
|
||||||
|
|
||||||
// Apply the effects of this block (with given index) on the UTXO set represented by coins
|
// Apply the effects of this block (with given index) on the UTXO set represented by coins
|
||||||
bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false);
|
bool ConnectBlock(CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false);
|
||||||
|
Loading…
Reference in New Issue
Block a user