Merge pull request #3576 from mempool/mononaut/fix-difficulty-adjustment

Fix difficulty adjustment bugs
This commit is contained in:
softsimon 2023-03-26 18:02:30 +09:00 committed by GitHub
commit 6722e45109
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 19 additions and 17 deletions

View file

@ -24,12 +24,11 @@ export function calcDifficultyAdjustment(
network: string, network: string,
latestBlockTimestamp: number, latestBlockTimestamp: number,
): DifficultyAdjustment { ): DifficultyAdjustment {
const ESTIMATE_LAG_BLOCKS = 146; // For first 7.2% of epoch, don't estimate.
const EPOCH_BLOCK_LENGTH = 2016; // Bitcoin mainnet const EPOCH_BLOCK_LENGTH = 2016; // Bitcoin mainnet
const BLOCK_SECONDS_TARGET = 600; // Bitcoin mainnet const BLOCK_SECONDS_TARGET = 600; // Bitcoin mainnet
const TESTNET_MAX_BLOCK_SECONDS = 1200; // Bitcoin testnet const TESTNET_MAX_BLOCK_SECONDS = 1200; // Bitcoin testnet
const diffSeconds = nowSeconds - DATime; const diffSeconds = Math.max(0, nowSeconds - DATime);
const blocksInEpoch = (blockHeight >= 0) ? blockHeight % EPOCH_BLOCK_LENGTH : 0; const blocksInEpoch = (blockHeight >= 0) ? blockHeight % EPOCH_BLOCK_LENGTH : 0;
const progressPercent = (blockHeight >= 0) ? blocksInEpoch / EPOCH_BLOCK_LENGTH * 100 : 100; const progressPercent = (blockHeight >= 0) ? blocksInEpoch / EPOCH_BLOCK_LENGTH * 100 : 100;
const remainingBlocks = EPOCH_BLOCK_LENGTH - blocksInEpoch; const remainingBlocks = EPOCH_BLOCK_LENGTH - blocksInEpoch;
@ -37,18 +36,16 @@ export function calcDifficultyAdjustment(
const expectedBlocks = diffSeconds / BLOCK_SECONDS_TARGET; const expectedBlocks = diffSeconds / BLOCK_SECONDS_TARGET;
let difficultyChange = 0; let difficultyChange = 0;
let timeAvgSecs = diffSeconds / blocksInEpoch; let timeAvgSecs = blocksInEpoch ? diffSeconds / blocksInEpoch : BLOCK_SECONDS_TARGET;
// Only calculate the estimate once we have 7.2% of blocks in current epoch
if (blocksInEpoch >= ESTIMATE_LAG_BLOCKS) { difficultyChange = (BLOCK_SECONDS_TARGET / timeAvgSecs - 1) * 100;
difficultyChange = (BLOCK_SECONDS_TARGET / timeAvgSecs - 1) * 100; // Max increase is x4 (+300%)
// Max increase is x4 (+300%) if (difficultyChange > 300) {
if (difficultyChange > 300) { difficultyChange = 300;
difficultyChange = 300; }
} // Max decrease is /4 (-75%)
// Max decrease is /4 (-75%) if (difficultyChange < -75) {
if (difficultyChange < -75) { difficultyChange = -75;
difficultyChange = -75;
}
} }
// Testnet difficulty is set to 1 after 20 minutes of no blocks, // Testnet difficulty is set to 1 after 20 minutes of no blocks,

View file

@ -211,6 +211,7 @@ class WebsocketHandler {
if (!_blocks) { if (!_blocks) {
_blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT); _blocks = blocks.getBlocks().slice(-config.MEMPOOL.INITIAL_BLOCKS_AMOUNT);
} }
const da = difficultyAdjustment.getDifficultyAdjustment();
return { return {
'mempoolInfo': memPool.getMempoolInfo(), 'mempoolInfo': memPool.getMempoolInfo(),
'vBytesPerSecond': memPool.getVBytesPerSecond(), 'vBytesPerSecond': memPool.getVBytesPerSecond(),
@ -220,7 +221,7 @@ class WebsocketHandler {
'transactions': memPool.getLatestTransactions(), 'transactions': memPool.getLatestTransactions(),
'backendInfo': backendInfo.getBackendInfo(), 'backendInfo': backendInfo.getBackendInfo(),
'loadingIndicators': loadingIndicators.getLoadingIndicators(), 'loadingIndicators': loadingIndicators.getLoadingIndicators(),
'da': difficultyAdjustment.getDifficultyAdjustment(), 'da': da?.previousTime ? da : undefined,
'fees': feeApi.getRecommendedFee(), 'fees': feeApi.getRecommendedFee(),
...this.extraInitProperties ...this.extraInitProperties
}; };
@ -278,7 +279,9 @@ class WebsocketHandler {
response['mempoolInfo'] = mempoolInfo; response['mempoolInfo'] = mempoolInfo;
response['vBytesPerSecond'] = vBytesPerSecond; response['vBytesPerSecond'] = vBytesPerSecond;
response['transactions'] = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx)); response['transactions'] = newTransactions.slice(0, 6).map((tx) => Common.stripTransaction(tx));
response['da'] = da; if (da?.previousTime) {
response['da'] = da;
}
response['fees'] = recommendedFees; response['fees'] = recommendedFees;
} }
@ -505,7 +508,7 @@ class WebsocketHandler {
const response = { const response = {
'block': block, 'block': block,
'mempoolInfo': memPool.getMempoolInfo(), 'mempoolInfo': memPool.getMempoolInfo(),
'da': da, 'da': da?.previousTime ? da : undefined,
'fees': fees, 'fees': fees,
}; };

View file

@ -309,9 +309,11 @@ export interface IDifficultyAdjustment {
remainingBlocks: number; remainingBlocks: number;
remainingTime: number; remainingTime: number;
previousRetarget: number; previousRetarget: number;
previousTime: number;
nextRetargetHeight: number; nextRetargetHeight: number;
timeAvg: number; timeAvg: number;
timeOffset: number; timeOffset: number;
expectedBlocks: number;
} }
export interface IndexedDifficultyAdjustment { export interface IndexedDifficultyAdjustment {