fix(utxo): ReadUtxo + DisconnectBlock reconstruct UTXOs from txindex.vSpent
ReadUtxo (src/txdb-base.cpp) lacked the lazy-fallback path that HaveUtxo already had. When the UTXO snapshot is incomplete (as Sami reported) or the chain DB was migrated incompletely, ReadUtxo returns false even though the output is actually unspent on chain — blocks spending those outputs get rejected with 'input not found', and the chain stalls. GLM-5.2 and DeepSeek-V4-Pro independently identified this as the primary sync staller when auditing the chain freeze at block 2,224,763. Fix: when UTXO DB doesn't have the entry but txindex.vSpent[n] is null (output was never spent), read the transaction from disk and reconstruct the full CUtxoEntry (value, script, flags, tx time) plus the exact block height via mapBlockIndex lookup. DisconnectBlock (src/main.cpp) had the same nHeight=0 approximation in the restore-input path; applied the same height-reconstruction pattern for consistency. Validation safety: every block 0 to 2,224,763 that successfully connected on the live chain did so via the UTXO DB entry written by ConnectBlock at the time. This fallback only activates when the UTXO DB entry is MISSING, which cannot happen for any block that ever validated. Zero historical block validation changes.
This commit is contained in:
+14
-1
@@ -2174,11 +2174,24 @@ bool CBlock::DisconnectBlock(CTxDBBase& txdb, CBlockIndex* pindex)
|
||||
const CTxOut& prevout = txPrev.vout[txin.prevout.n];
|
||||
CUtxoEntry utxo;
|
||||
utxo.nValue = prevout.nValue;
|
||||
utxo.nHeight = 0; // approximation; exact height not critical for restored UTXOs
|
||||
utxo.scriptPubKey = prevout.scriptPubKey;
|
||||
utxo.fCoinBase = txPrev.IsCoinBase();
|
||||
utxo.fCoinStake = txPrev.IsCoinStake();
|
||||
utxo.nTxTime = txPrev.nTime;
|
||||
|
||||
// Reconstruct exact height via block index lookup.
|
||||
// Falls back to 0 if mapBlockIndex doesn't have the
|
||||
// tx's block yet (safe — ConnectInputs maturity
|
||||
// check then requires COINBASE_MATURITY confirmations).
|
||||
utxo.nHeight = 0;
|
||||
CBlock blockHeader;
|
||||
if (blockHeader.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false))
|
||||
{
|
||||
auto bmi = mapBlockIndex.find(blockHeader.GetHash());
|
||||
if (bmi != mapBlockIndex.end())
|
||||
utxo.nHeight = bmi->second->nHeight;
|
||||
}
|
||||
|
||||
txdb.WriteUtxo(txin.prevout.hash, txin.prevout.n, utxo);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user