From 325867208f5359b39065c500c7f8587d440fe64e Mon Sep 17 00:00:00 2001 From: mpicco Date: Mon, 10 Feb 2020 12:17:17 -0300 Subject: [PATCH] Calculate hashrate based on cummulativeDifficulty (block diff + uncles diff). If no cummulativeDifficulty field is present, use an estimation based in the block diff * uncle count --- src/api/lib/hashrateCalculator.js | 6 +- src/api/modules/ExtendedStats.js | 6 +- test/services/hashrateCalculator.js | 340 +++++++++++++++++++++------- 3 files changed, 265 insertions(+), 87 deletions(-) diff --git a/src/api/lib/hashrateCalculator.js b/src/api/lib/hashrateCalculator.js index a10107e8..76c7aebe 100644 --- a/src/api/lib/hashrateCalculator.js +++ b/src/api/lib/hashrateCalculator.js @@ -12,7 +12,11 @@ export class HashrateCalculator { diffPerMiner[block.miner] = new BigNumber(0) } - const bnDiff = new BigNumber(block.difficulty) + // cummulativeDifficulty = block.diff + sum(uncle.diff) + // If no cummulativeDifficulty is present, use an estimation under the supposition that the block diff + // is similar to its uncles' difficulty. + const bnDiff = block.cummulativeDifficulty ? new BigNumber(block.cummulativeDifficulty) : + new BigNumber(block.difficulty).multipliedBy((block.uncles ? block.uncles.length : 0) + 1) diffPerMiner[block.miner] = diffPerMiner[block.miner].plus(bnDiff) } diff --git a/src/api/modules/ExtendedStats.js b/src/api/modules/ExtendedStats.js index eb3d84ed..5f453db8 100644 --- a/src/api/modules/ExtendedStats.js +++ b/src/api/modules/ExtendedStats.js @@ -142,7 +142,7 @@ export class ExtendedStats extends DataCollectorItem { const start = end - timeSpan const blocks = await this.db.find({ timestamp: { $gte: start, $lte: end } }) - .project({ _id: 0, miner: 1, timestamp: 1, difficulty: 1 }) + .project({ _id: 0, miner: 1, timestamp: 1, difficulty: 1, cummulativeDifficulty: 1, uncles: 1 }) .toArray() extendedStats.hashrates[period] = this.hashrateCalculator.hashrates(blocks, timeSpan) @@ -162,7 +162,7 @@ export class ExtendedStats extends DataCollectorItem { const timeSpan = PERIODS[period].timeSpan const blocks = await this.db.find({ timestamp: { $gte: blockDate - timeSpan, $lte: blockDate } }) - .project({ _id: 0, miner: 1, difficulty: 1 }) + .project({ _id: 0, miner: 1, difficulty: 1, cummulativeDifficulty: 1, uncles: 1 }) .toArray() hashrates[period] = this.hashrateCalculator.hashrates(blocks, timeSpan) @@ -182,7 +182,7 @@ export class ExtendedStats extends DataCollectorItem { const start = end - timeSpan const blocks = await this.db.find({ timestamp: { $gte: start, $lte: end } }) - .project({ _id: 0, timestamp: 1, difficulty: 1 }) + .project({ _id: 0, timestamp: 1, difficulty: 1, cummulativeDifficulty: 1, uncles: 1 }) .toArray() difficulties[period] = this.difficultyCalculator.difficulties(blocks, start, end, DIFFICULTY_BUCKET_SIZE) diff --git a/test/services/hashrateCalculator.js b/test/services/hashrateCalculator.js index 8c9a1b67..92dd8cd6 100644 --- a/test/services/hashrateCalculator.js +++ b/test/services/hashrateCalculator.js @@ -6,7 +6,7 @@ describe('hashrateCalculator', () => { const exa = (n) => new BigNumber(`${n}e18`) const hexExa = (n) => `0x${new BigNumber(`${n}e18`).toString(16)}` - context('hashratePercentagePerMiner', () => { + context('hashratePercentagePerMiner, negative cases', () => { it('returns an empty object when argument is not an array', () => { const calc = new HashrateCalculator() @@ -22,12 +22,32 @@ describe('hashrateCalculator', () => { assert.deepEqual(hashrate, {}) }) + }) + + context('hashratePerMiner, negative cases', () => { + it('returns an empty object when argument is not an array', () => { + const calc = new HashrateCalculator() + + const hashrate = calc.hashratePerMiner(0) + + assert.deepEqual(hashrate, {}) + }) + + it('returns an empty object when no blocks', () => { + const calc = new HashrateCalculator() + + const hashrate = calc.hashratePerMiner([], 0) + + assert.deepEqual(hashrate, {}) + }) + }) + context('hashratePercentagePerMiner, cummulativeDifficulty', () => { it('returns 1 for only one miner and 1 block', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -40,8 +60,8 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0b', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cummulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -55,10 +75,10 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e3' }, - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e2' }, - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e1' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e3' }, + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e2' }, + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e1' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -71,10 +91,10 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0b', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0c', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0d', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0c', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0d', cummulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -90,10 +110,10 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0b', difficulty: '0x13b768fb4f683f5fd5' }, - { miner: '0x0c', difficulty: '0x0f54a7810c212d7df0' }, - { miner: '0x0d', difficulty: '0x0f54a7810c212d7df0' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cummulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0c', cummulativeDifficulty: '0x0f54a7810c212d7df0' }, + { miner: '0x0d', cummulativeDifficulty: '0x0f54a7810c212d7df0' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -109,17 +129,17 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0b', difficulty: '0x13b768fb4f683f5fd5' }, - { miner: '0x0a', difficulty: '0x13b768fb4f683f5fd5' }, - { miner: '0x0c', difficulty: '0x10050a0cc225820cdd' }, - { miner: '0x0d', difficulty: '0x10050a0cc225820cdd' }, - { miner: '0x0d', difficulty: '0x0f54a7810c212d7df0' }, - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0d', difficulty: '0x0f54a7810c212d7d00' }, - { miner: '0x0c', difficulty: '0x10050a0cc225820cdd' }, - { miner: '0x0b', difficulty: '0x10050a0cc225820fff' }, - { miner: '0x0d', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cummulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0a', cummulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0c', cummulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0d', cummulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0d', cummulativeDifficulty: '0x0f54a7810c212d7df0' }, + { miner: '0x0a', cummulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0d', cummulativeDifficulty: '0x0f54a7810c212d7d00' }, + { miner: '0x0c', cummulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0b', cummulativeDifficulty: '0x10050a0cc225820fff' }, + { miner: '0x0d', cummulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -132,30 +152,14 @@ describe('hashrateCalculator', () => { }) }) - context('hashratePerMiner', () => { + context('hashratePerMiner, cummulativeDifficulty', () => { const START = 1 - it('returns an empty object when argument is not an array', () => { - const calc = new HashrateCalculator() - - const hashrate = calc.hashratePerMiner(0) - - assert.deepEqual(hashrate, {}) - }) - - it('returns an empty object when no blocks', () => { - const calc = new HashrateCalculator() - - const hashrate = calc.hashratePerMiner([], 0) - - assert.deepEqual(hashrate, {}) - }) - it('returns the same diff as hashrate for one block', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: exa(1), timestamp: START } + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START } ] const hashrate = calc.hashratePerMiner(blocks, 0) @@ -168,11 +172,11 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: exa(1), timestamp: START }, - { miner: '0x0a', difficulty: exa(1), timestamp: START + 1 }, - { miner: '0x0a', difficulty: exa(1), timestamp: START + 2 }, - { miner: '0x0a', difficulty: exa(1), timestamp: START + 3 }, - { miner: '0x0a', difficulty: exa(1), timestamp: START + 4 } + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START + 1 }, + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START + 2 }, + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START + 3 }, + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START + 4 } ] const hashrate = calc.hashratePerMiner(blocks, (START + 4) - START) @@ -185,17 +189,17 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: exa(1), timestamp: START }, - { miner: '0x0b', difficulty: exa(2), timestamp: START + 1 }, - { miner: '0x0a', difficulty: exa(3), timestamp: START + 2 }, - { miner: '0x0c', difficulty: exa(4), timestamp: START + 3 }, - { miner: '0x0d', difficulty: exa(5), timestamp: START + 4 }, - { miner: '0x0a', difficulty: exa(6), timestamp: START + 5 }, - { miner: '0x0a', difficulty: exa(7), timestamp: START + 6 }, - { miner: '0x0b', difficulty: exa(8), timestamp: START + 7 }, - { miner: '0x0a', difficulty: exa(9), timestamp: START + 8 }, - { miner: '0x0c', difficulty: exa(10), timestamp: START + 9 }, - { miner: '0x0c', difficulty: exa(11), timestamp: START + 10 } + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0b', cummulativeDifficulty: exa(2), timestamp: START + 1 }, + { miner: '0x0a', cummulativeDifficulty: exa(3), timestamp: START + 2 }, + { miner: '0x0c', cummulativeDifficulty: exa(4), timestamp: START + 3 }, + { miner: '0x0d', cummulativeDifficulty: exa(5), timestamp: START + 4 }, + { miner: '0x0a', cummulativeDifficulty: exa(6), timestamp: START + 5 }, + { miner: '0x0a', cummulativeDifficulty: exa(7), timestamp: START + 6 }, + { miner: '0x0b', cummulativeDifficulty: exa(8), timestamp: START + 7 }, + { miner: '0x0a', cummulativeDifficulty: exa(9), timestamp: START + 8 }, + { miner: '0x0c', cummulativeDifficulty: exa(10), timestamp: START + 9 }, + { miner: '0x0c', cummulativeDifficulty: exa(11), timestamp: START + 10 } ] const hashrate = calc.hashratePerMiner(blocks, (START + 10) - START) @@ -215,17 +219,17 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: exa(1), timestamp: START }, - { miner: '0x0b', difficulty: exa(2), timestamp: START + 1 }, - { miner: '0x0a', difficulty: exa(3), timestamp: START + 2 }, - { miner: '0x0c', difficulty: exa(4), timestamp: START + 3 }, - { miner: '0x0d', difficulty: exa(5), timestamp: START + 4 }, - { miner: '0x0a', difficulty: exa(6), timestamp: START + 5 }, - { miner: '0x0a', difficulty: exa(7), timestamp: START + 6 }, - { miner: '0x0b', difficulty: exa(8), timestamp: START + 7 }, - { miner: '0x0a', difficulty: exa(9), timestamp: START + 8 }, - { miner: '0x0c', difficulty: exa(10), timestamp: START + 9 }, - { miner: '0x0c', difficulty: exa(11), timestamp: START + 10 } + { miner: '0x0a', cummulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0b', cummulativeDifficulty: exa(2), timestamp: START + 1 }, + { miner: '0x0a', cummulativeDifficulty: exa(3), timestamp: START + 2 }, + { miner: '0x0c', cummulativeDifficulty: exa(4), timestamp: START + 3 }, + { miner: '0x0d', cummulativeDifficulty: exa(5), timestamp: START + 4 }, + { miner: '0x0a', cummulativeDifficulty: exa(6), timestamp: START + 5 }, + { miner: '0x0a', cummulativeDifficulty: exa(7), timestamp: START + 6 }, + { miner: '0x0b', cummulativeDifficulty: exa(8), timestamp: START + 7 }, + { miner: '0x0a', cummulativeDifficulty: exa(9), timestamp: START + 8 }, + { miner: '0x0c', cummulativeDifficulty: exa(10), timestamp: START + 9 }, + { miner: '0x0c', cummulativeDifficulty: exa(11), timestamp: START + 10 } ] const hashrate = calc.hashrates(blocks, (START + 10) - START) @@ -242,17 +246,17 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: hexExa(1), timestamp: START }, - { miner: '0x0b', difficulty: hexExa(2), timestamp: START + 1 }, - { miner: '0x0a', difficulty: hexExa(3), timestamp: START + 2 }, - { miner: '0x0c', difficulty: hexExa(4), timestamp: START + 3 }, - { miner: '0x0d', difficulty: hexExa(5), timestamp: START + 4 }, - { miner: '0x0a', difficulty: hexExa(6), timestamp: START + 5 }, - { miner: '0x0a', difficulty: hexExa(7), timestamp: START + 6 }, - { miner: '0x0b', difficulty: hexExa(8), timestamp: START + 7 }, - { miner: '0x0a', difficulty: hexExa(9), timestamp: START + 8 }, - { miner: '0x0c', difficulty: hexExa(10), timestamp: START + 9 }, - { miner: '0x0c', difficulty: hexExa(11), timestamp: START + 10 } + { miner: '0x0a', cummulativeDifficulty: hexExa(1), timestamp: START }, + { miner: '0x0b', cummulativeDifficulty: hexExa(2), timestamp: START + 1 }, + { miner: '0x0a', cummulativeDifficulty: hexExa(3), timestamp: START + 2 }, + { miner: '0x0c', cummulativeDifficulty: hexExa(4), timestamp: START + 3 }, + { miner: '0x0d', cummulativeDifficulty: hexExa(5), timestamp: START + 4 }, + { miner: '0x0a', cummulativeDifficulty: hexExa(6), timestamp: START + 5 }, + { miner: '0x0a', cummulativeDifficulty: hexExa(7), timestamp: START + 6 }, + { miner: '0x0b', cummulativeDifficulty: hexExa(8), timestamp: START + 7 }, + { miner: '0x0a', cummulativeDifficulty: hexExa(9), timestamp: START + 8 }, + { miner: '0x0c', cummulativeDifficulty: hexExa(10), timestamp: START + 9 }, + { miner: '0x0c', cummulativeDifficulty: hexExa(11), timestamp: START + 10 } ] const hashrate = calc.hashrates(blocks, (START + 10) - START) @@ -264,4 +268,174 @@ describe('hashrateCalculator', () => { }) }) }) + + context('hashratePercentagePerMiner, difficulty and uncles estimate', () => { + it('returns 2/3 for a miner with one uncle when two miners with same difficulty', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: ['0x01'] }, + { miner: '0x0b', difficulty: '0x11f36beaf6690ac7e6', uncles: [] } + ] + const hashrate = calc.hashratePercentagePerMiner(blocks) + + assert.deepEqual(hashrate, { + '0x0a': 0.667, + '0x0b': 0.333 + }) + }) + + it('returns 1 for one miner that mined more than one block', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: ['0x01'] }, + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e3', uncles: [] }, + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e2', uncles: [] }, + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e1', uncles: [] } + ] + const hashrate = calc.hashratePercentagePerMiner(blocks) + + assert.deepEqual(hashrate, { + '0x0a': 1 + }) + }) + + it('returns 2/5 for a miner with one uncle when four miners that mined one block with the same block difficulty each', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: ['0x0a'] }, + { miner: '0x0b', difficulty: '0x11f36beaf6690ac7e6', uncles: [] }, + { miner: '0x0c', difficulty: '0x11f36beaf6690ac7e6', uncles: [] }, + { miner: '0x0d', difficulty: '0x11f36beaf6690ac7e6', uncles: [] } + ] + const hashrate = calc.hashratePercentagePerMiner(blocks) + + assert.deepEqual(hashrate, { + '0x0a': 2/5, + '0x0b': 1/5, + '0x0c': 1/5, + '0x0d': 1/5 + }) + }) + + it('returns the corresponding value when four miners mine one block each with different difficulty', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: ['0x0a'] }, + { miner: '0x0b', difficulty: '0x13b768fb4f683f5fd5', uncles: [] }, + { miner: '0x0c', difficulty: '0x0f54a7810c212d7df0', uncles: [] }, + { miner: '0x0d', difficulty: '0x0f54a7810c212d7df0', uncles: [] } + ] + const hashrate = calc.hashratePercentagePerMiner(blocks) + + assert.deepEqual(hashrate, { + '0x0a': 0.416, + '0x0b': 0.229, + '0x0c': 0.178, + '0x0d': 0.178 + }) + }) + + it('returns the corresponding value when four miners mine several blocks each with different difficulty', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: ['0x0a'] }, + { miner: '0x0b', difficulty: '0x13b768fb4f683f5fd5', uncles: [] }, + { miner: '0x0a', difficulty: '0x13b768fb4f683f5fd5', uncles: [] }, + { miner: '0x0c', difficulty: '0x10050a0cc225820cdd', uncles: [] }, + { miner: '0x0d', difficulty: '0x10050a0cc225820cdd', uncles: ['0x01', '0x02'] }, + { miner: '0x0d', difficulty: '0x0f54a7810c212d7df0', uncles: [] }, + { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6', uncles: [] }, + { miner: '0x0d', difficulty: '0x0f54a7810c212d7d00', uncles: ['0x0a'] }, + { miner: '0x0c', difficulty: '0x10050a0cc225820cdd', uncles: [] }, + { miner: '0x0b', difficulty: '0x10050a0cc225820fff', uncles: [] }, + { miner: '0x0d', difficulty: '0x11f36beaf6690ac7e6', uncles: [] } + ] + const hashrate = calc.hashratePercentagePerMiner(blocks) + + assert.deepEqual(hashrate, { + '0x0a': 0.290, + '0x0b': 0.141, + '0x0c': 0.126, + '0x0d': 0.442 + }) + }) + }) + + context('hashratePerMiner, cummulativeDifficulty', () => { + const START = 1 + + it('returns the same diff as hashrate for one block', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: exa(1), uncles: [], timestamp: START } + ] + const hashrate = calc.hashratePerMiner(blocks, 0) + + assert.deepEqual(hashrate, { + '0x0a': '1.000 EHs' + }) + }) + + it('returns the same diff as hashrate for one block, defaults to 0 uncles when no uncles array present', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: exa(1), timestamp: START } + ] + const hashrate = calc.hashratePerMiner(blocks, 0) + + assert.deepEqual(hashrate, { + '0x0a': '1.000 EHs' + }) + }) + + it('returns the cumulative diff divided the time elapsed between first and last block for one miner', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: exa(1), uncles: ['0x0a', '0x0b'], timestamp: START }, + { miner: '0x0a', difficulty: exa(1), uncles: [], timestamp: START + 1 }, + { miner: '0x0a', difficulty: exa(1), uncles: [], timestamp: START + 2 }, + { miner: '0x0a', difficulty: exa(1), uncles: [], timestamp: START + 3 }, + { miner: '0x0a', difficulty: exa(1), uncles: [], timestamp: START + 4 } + ] + const hashrate = calc.hashratePerMiner(blocks, (START + 4) - START) + + assert.deepEqual(hashrate, { + '0x0a': '1.750 EHs' + }) + }) + + it('returns the cumulative diff divided the time elapsed between first and last block for multiple miners', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', difficulty: exa(1), uncles: ['0x0a'], timestamp: START }, + { miner: '0x0b', difficulty: exa(2), uncles: [], timestamp: START + 1 }, + { miner: '0x0a', difficulty: exa(3), uncles: [], timestamp: START + 2 }, + { miner: '0x0c', difficulty: exa(4), uncles: [], timestamp: START + 3 }, + { miner: '0x0d', difficulty: exa(5), uncles: [], timestamp: START + 4 }, + { miner: '0x0a', difficulty: exa(6), uncles: ['0x0b'], timestamp: START + 5 }, + { miner: '0x0a', difficulty: exa(7), uncles: [], timestamp: START + 6 }, + { miner: '0x0b', difficulty: exa(8), uncles: [], timestamp: START + 7 }, + { miner: '0x0a', difficulty: exa(9), uncles: ['0x0c', '0x0d'], timestamp: START + 8 }, + { miner: '0x0c', difficulty: exa(10), uncles: [], timestamp: START + 9 }, + { miner: '0x0c', difficulty: exa(11), uncles: [], timestamp: START + 10 } + ] + const hashrate = calc.hashratePerMiner(blocks, (START + 10) - START) + + assert.deepEqual(hashrate, { + '0x0a': '5.100 EHs', + '0x0b': '1.000 EHs', + '0x0c': '2.500 EHs', + '0x0d': '0.500 EHs' + }) + }) + }) })