Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions js/charts.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,15 +240,17 @@ const Charts = (function() {
},
});

// Consecutive runs (from actual baseline)
// Consecutive runs (from actual baseline). _consecutive is the run
// length INCLUDING the current block (a lone block is a run of 1),
// consistent with the simulated scenarios' counters.
const runCounts = {};
for (const b of data) {
if (b._consecutive > 0) runCounts[b._consecutive] = (runCounts[b._consecutive] || 0) + 1;
if (b._consecutive > 1) runCounts[b._consecutive] = (runCounts[b._consecutive] || 0) + 1;
}
const maxRun = Math.max(...Object.keys(runCounts).map(Number), 0);
const labels = [], values = [];
for (let i = 1; i <= Math.min(maxRun, 10); i++) { labels.push(`${i} consecutive`); values.push(runCounts[i] || 0); }
if (maxRun > 10) { labels.push('10+'); values.push(Object.entries(runCounts).filter(([k]) => Number(k) > 10).reduce((s, [, v]) => s + v, 0)); }
for (let i = 2; i <= Math.min(maxRun, 10); i++) { labels.push(`Run of ${i}`); values.push(runCounts[i] || 0); }
if (maxRun > 10) { labels.push('Run of 10+'); values.push(Object.entries(runCounts).filter(([k]) => Number(k) > 10).reduce((s, [, v]) => s + v, 0)); }

get('chartConsecutiveRuns', {
type: 'bar',
Expand Down
26 changes: 20 additions & 6 deletions js/simulation.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,11 @@ function precomputeBlockData(blocks) {
}

function countConsecutiveSameAlgo(blocks, currentIndex, algo) {
if (currentIndex === 0) return 0;
// Run length INCLUDING the current block: a lone block is a run of 1, the
// second of a streak is 2, etc. (consistent with the simulated scenario
// counters, so "Consecutive max" means the same thing on every tab).
let count = 0;
for (let lookback = currentIndex - 1; lookback >= 0; lookback--) {
for (let lookback = currentIndex; lookback >= 0; lookback--) {
if (blocks[lookback].pow_algo !== algo) break;
count++;
}
Expand Down Expand Up @@ -164,8 +166,13 @@ function runCompetition(blocks, scenario, seed) {
simulatedDifficulty = algoRates.find(entry => entry.algo === winningAlgo).targetDifficulty;
}

// consecutiveCount = run length of the last mined block (>= 1). A block
// extending the run is at position r = consecutiveCount + 1 and pays
// 2^(r-1) = 2^consecutiveCount: the second consecutive block doubles,
// the third quadruples, etc. (TIP: m = 2^(r-1)). Any other algorithm
// resets the run to length 1.
if (winningAlgo === lastWinner) consecutiveCount++;
else consecutiveCount = 0;
else consecutiveCount = 1; // a fresh winner starts a run of length 1
lastWinner = winningAlgo;

simulatedTimestamp += simulatedSolveTime;
Expand Down Expand Up @@ -230,6 +237,9 @@ function computeAlgoRates(windows, hashRateHistory, scenario, lastWinner, consec
}

function applyPenaltyIfActive(window, algoConfig, scenario, algoId, lastWinner, consecutiveCount) {
// consecutiveCount is the run length ending at the previous block, so the
// block being mined is at position r = consecutiveCount + 1 and pays
// 2^(r-1) = 2^consecutiveCount. Fresh winners (consecutive = 0) pay base.
const isConsecutiveWinner = (lastWinner === algoId);
const consecutive = isConsecutiveWinner ? consecutiveCount : 0;

Expand Down Expand Up @@ -292,7 +302,7 @@ function buildResultObject(block, winningAlgo, simulatedDifficulty, simulatedSol
actualMainChainBT: block._mainChainBlockTime,
actualTimestamp: block.timestamp,
consecutive: consecutiveCount,
penaltyMultiplier: (penaltyEnabled && consecutiveCount > 0) ? Math.pow(Number(PENALTY_BASE), consecutiveCount) : 1,
penaltyMultiplier: (penaltyEnabled && consecutiveCount > 0) ? Math.pow(Number(PENALTY_BASE), Math.max(consecutiveCount - 1, 0)) : 1,
window: windowSize,
penalty: penaltyEnabled,
};
Expand Down Expand Up @@ -397,7 +407,8 @@ function runCompetitionSW(blocks, scenario, seed) {
block, winningAlgo, simulatedDifficulty, simulatedSolveTime,
Math.floor(simulatedTimestamp), consecutiveCount, scenario.window, scenario.penalty
);
// Override with the capped modifier (buildResultObject would compute 2^n uncapped).
// Override with the capped modifier (buildResultObject's default is the
// uncapped 2^(consecutive-1)).
result.penaltyMultiplier = winnerModifier > 1n ? Number(winnerModifier) : 1;
results.push(result);
}
Expand Down Expand Up @@ -510,8 +521,11 @@ function runCompetitionWtema(blocks, scenario, seed) {
simulatedDifficulty = algoRates.find(entry => entry.algo === winningAlgo).targetDifficulty;
}

// No penalty here; consecutiveCount is still run length (>= 1, including
// the current block) so the "Consecutive max" stat matches the other
// scenarios and the actual-data tab.
if (winningAlgo === lastWinner) consecutiveCount++;
else consecutiveCount = 0;
else consecutiveCount = 1;
lastWinner = winningAlgo;

simulatedTimestamp += simulatedSolveTime;
Expand Down