mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 05:32:22 +02:00
fees: Return false for incompatible fee estimates
policy_estimator_io deliberately reuses a CBlockPolicyEstimator because constructing one for every fuzz input severely reduces throughput. However, Read() returns true for an incompatible old fee estimates file without replacing the estimator state. The target then calls Write() with state loaded by a previous input, making coverage depend on corpus order. Return false for incompatible files so the target skips Write() when no state was loaded. This keeps the estimator reuse optimization instead of resetting the expensive object before every fuzz input. For the in-tree production caller, incompatible files remain non-fatal and the estimator still starts from its default state. Read() now reports failure, so startup emits one additional non-fatal warning. Node startup and estimator state are unchanged, as is RPC behavior. Co-authored-by: maflcko <6399679+maflcko@users.noreply.github.com>
This commit is contained in:
@@ -1008,51 +1008,51 @@ bool CBlockPolicyEstimator::Read(AutoFile& filein)
|
||||
if (nVersionRequired > CURRENT_FEES_FILE_VERSION) {
|
||||
throw std::runtime_error{strprintf("File version (%d) too high to be read.", nVersionRequired)};
|
||||
}
|
||||
if (nVersionRequired < CURRENT_FEES_FILE_VERSION) {
|
||||
throw std::runtime_error{strprintf("File version (%d) incompatible: Too old to be read", nVersionRequired)};
|
||||
}
|
||||
|
||||
// Read fee estimates file into temporary variables so existing data
|
||||
// structures aren't corrupted if there is an exception.
|
||||
unsigned int nFileBestSeenHeight;
|
||||
filein >> nFileBestSeenHeight;
|
||||
|
||||
if (nVersionRequired < CURRENT_FEES_FILE_VERSION) {
|
||||
LogWarning("Incompatible old fee estimation data (non-fatal). Version: %d", nVersionRequired);
|
||||
} else { // nVersionRequired == CURRENT_FEES_FILE_VERSION
|
||||
unsigned int nFileHistoricalFirst, nFileHistoricalBest;
|
||||
filein >> nFileHistoricalFirst >> nFileHistoricalBest;
|
||||
if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
|
||||
throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
|
||||
}
|
||||
std::vector<double> fileBuckets;
|
||||
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
|
||||
size_t numBuckets = fileBuckets.size();
|
||||
if (numBuckets <= 1 || numBuckets > 1000) {
|
||||
throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
|
||||
}
|
||||
|
||||
std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
||||
std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
||||
std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
||||
fileFeeStats->Read(filein, numBuckets);
|
||||
fileShortStats->Read(filein, numBuckets);
|
||||
fileLongStats->Read(filein, numBuckets);
|
||||
|
||||
// Fee estimates file parsed correctly
|
||||
// Copy buckets from file and refresh our bucketmap
|
||||
buckets = fileBuckets;
|
||||
bucketMap.clear();
|
||||
for (unsigned int i = 0; i < buckets.size(); i++) {
|
||||
bucketMap[buckets[i]] = i;
|
||||
}
|
||||
|
||||
// Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
|
||||
feeStats = std::move(fileFeeStats);
|
||||
shortStats = std::move(fileShortStats);
|
||||
longStats = std::move(fileLongStats);
|
||||
|
||||
nBestSeenHeight = nFileBestSeenHeight;
|
||||
historicalFirst = nFileHistoricalFirst;
|
||||
historicalBest = nFileHistoricalBest;
|
||||
// nVersionRequired == CURRENT_FEES_FILE_VERSION
|
||||
unsigned int nFileHistoricalFirst, nFileHistoricalBest;
|
||||
filein >> nFileHistoricalFirst >> nFileHistoricalBest;
|
||||
if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
|
||||
throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
|
||||
}
|
||||
std::vector<double> fileBuckets;
|
||||
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
|
||||
size_t numBuckets = fileBuckets.size();
|
||||
if (numBuckets <= 1 || numBuckets > 1000) {
|
||||
throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
|
||||
}
|
||||
|
||||
std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
||||
std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
||||
std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
||||
fileFeeStats->Read(filein, numBuckets);
|
||||
fileShortStats->Read(filein, numBuckets);
|
||||
fileLongStats->Read(filein, numBuckets);
|
||||
|
||||
// Fee estimates file parsed correctly
|
||||
// Copy buckets from file and refresh our bucketmap
|
||||
buckets = fileBuckets;
|
||||
bucketMap.clear();
|
||||
for (unsigned int i = 0; i < buckets.size(); i++) {
|
||||
bucketMap[buckets[i]] = i;
|
||||
}
|
||||
|
||||
// Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
|
||||
feeStats = std::move(fileFeeStats);
|
||||
shortStats = std::move(fileShortStats);
|
||||
longStats = std::move(fileLongStats);
|
||||
|
||||
nBestSeenHeight = nFileBestSeenHeight;
|
||||
historicalFirst = nFileHistoricalFirst;
|
||||
historicalBest = nFileHistoricalBest;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
LogWarning("Unable to read policy estimator data (non-fatal): %s", e.what());
|
||||
|
||||
Reference in New Issue
Block a user