diff --git a/dist/services/classes/Tx.js b/dist/services/classes/Tx.js index 6c3acd73..b0d424ca 100644 --- a/dist/services/classes/Tx.js +++ b/dist/services/classes/Tx.js @@ -22,6 +22,7 @@ class Tx extends _BcThing.BcThing { } catch (err) { return Promise.reject(err); } + } async getTx() { diff --git a/src/api/Api.js b/src/api/Api.js index 57211606..87c4d58d 100644 --- a/src/api/Api.js +++ b/src/api/Api.js @@ -139,12 +139,13 @@ class Api extends DataCollector { const stats = await Stats.run('getLatest') if (!stats) return - /* const ExtendedStats = this.getModule('ExtendedStats') - if (ExtendedStats) { - const blockNumber = parseInt(stats.blockNumber) - const extendedStats = await ExtendedStats.getExtendedStats(blockNumber) - Object.assign(stats, extendedStats) - } */ + const ExtendedStats = this.getModule('ExtendedStats') + if (ExtendedStats) { + const blockNumber = parseInt(stats.blockNumber) + const extendedStats = await ExtendedStats.getExtendedStats(blockNumber) + Object.assign(stats, extendedStats) + } + let circulatingSupply = stats.circulatingSupply || await this.getCirculatingSupplyFromDb() this.circulatingSupply = circulatingSupply this.stats = Object.assign({}, stats) diff --git a/src/api/lib/hashrateCalculator.js b/src/api/lib/hashrateCalculator.js index 451e850c..8f187db2 100644 --- a/src/api/lib/hashrateCalculator.js +++ b/src/api/lib/hashrateCalculator.js @@ -1,7 +1,7 @@ import { BigNumber } from 'bignumber.js' export const DECIMALS = 3 -export const EXA = new BigNumber('1e50') +export const EXA = new BigNumber('1e18') export class HashrateCalculator { difficultyPerMiner (blocks) { @@ -12,7 +12,11 @@ export class HashrateCalculator { diffPerMiner[block.miner] = new BigNumber(0) } - const bnDiff = new BigNumber(block.difficulty) + // cumulativeDifficulty = block.diff + sum(uncle.diff) + // If no cumulativeDifficulty is present, use an estimation under the supposition that the block diff + // is similar to its uncles' difficulty. + const bnDiff = block.cumulativeDifficulty ? new BigNumber(block.cumulativeDifficulty) : + new BigNumber(block.difficulty).multipliedBy((block.uncles ? block.uncles.length : 0) + 1) diffPerMiner[block.miner] = diffPerMiner[block.miner].plus(bnDiff) } @@ -35,7 +39,7 @@ export class HashrateCalculator { return percPerMiner } - hashratePerMiner (blocks) { + hashratePerMiner (blocks, timeSpan) { if (!Array.isArray(blocks)) { return {} } @@ -46,12 +50,12 @@ export class HashrateCalculator { let diffPerMiner = this.difficultyPerMiner(blocks) - let hashratePerMiner = this.innerHashratePerMiner(blocks, diffPerMiner) + let hashratePerMiner = this.innerHashratePerMiner(blocks, diffPerMiner, timeSpan) return hashratePerMiner } - hashrates (blocks) { + hashrates (blocks, timeSpan) { if (!Array.isArray(blocks)) { return {} } @@ -62,7 +66,7 @@ export class HashrateCalculator { let diffPerMiner = this.difficultyPerMiner(blocks) - let hashratePerMiner = this.innerHashratePerMiner(blocks, diffPerMiner) + let hashratePerMiner = this.innerHashratePerMiner(blocks, diffPerMiner, timeSpan) let percPerMiner = this.innerHashratePercentagePerMiner(diffPerMiner) let hashrates = {} @@ -88,14 +92,14 @@ export class HashrateCalculator { return percPerMiner } - innerHashratePerMiner (blocks, diffPerMiner) { - const start = new BigNumber(blocks[0].timestamp) - const end = new BigNumber(blocks[blocks.length - 1].timestamp) - const timeDiff = end.isGreaterThan(start) ? end.minus(start) : new BigNumber(1) + innerHashratePerMiner (blocks, diffPerMiner, timeSpan) { + if (timeSpan <= 0) + timeSpan = 1; let hashratePerMiner = {} for (const m of Object.keys(diffPerMiner)) { - const val = diffPerMiner[m].dividedBy(timeDiff).dividedBy(EXA).toFixed(DECIMALS) + const val = diffPerMiner[m].dividedBy(timeSpan).dividedBy(EXA).toFixed(DECIMALS) + hashratePerMiner[m] = `${val} EHs` } diff --git a/src/api/modules/ExtendedStats.js b/src/api/modules/ExtendedStats.js index a28f7f97..01775ec5 100644 --- a/src/api/modules/ExtendedStats.js +++ b/src/api/modules/ExtendedStats.js @@ -3,17 +3,17 @@ import { HashrateCalculator } from '../lib/hashrateCalculator' import { DifficultyCalculator } from '../lib/difficultyCalculator' // 1 hour bucket size -const DIFFICULTY_BUCKET_SIZE = 3600000 +const DIFFICULTY_BUCKET_SIZE = 3600 export const PERIODS = { '1w': { - timeLimit: 604800000 + timeSpan: 604800 }, '1d': { - timeLimit: 86400000 + timeSpan: 86400 }, '1h': { - timeLimit: 3600000 + timeSpan: 3600 } } @@ -138,14 +138,14 @@ export class ExtendedStats extends DataCollectorItem { const end = block.timestamp for (const period of Object.keys(PERIODS)) { - const timeLimit = PERIODS[period].timeLimit - const start = end - timeLimit + const timeSpan = PERIODS[period].timeSpan + 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, cumulativeDifficulty: 1, uncles: 1 }) .toArray() - extendedStats.hashrates[period] = this.hashrateCalculator.hashrates(blocks) + extendedStats.hashrates[period] = this.hashrateCalculator.hashrates(blocks, timeSpan) extendedStats.difficulties[period] = this.difficultyCalculator.difficulties(blocks, start, end, DIFFICULTY_BUCKET_SIZE) } @@ -159,13 +159,13 @@ export class ExtendedStats extends DataCollectorItem { const blockDate = block.timestamp for (const period of Object.keys(PERIODS)) { - const timeLimit = PERIODS[period].timeLimit + const timeSpan = PERIODS[period].timeSpan - const blocks = await this.db.find({ timestamp: { $gte: blockDate - timeLimit, $lte: blockDate } }) - .project({ _id: 0, miner: 1, difficulty: 1 }) + const blocks = await this.db.find({ timestamp: { $gte: blockDate - timeSpan, $lte: blockDate } }) + .project({ _id: 0, miner: 1, difficulty: 1, cumulativeDifficulty: 1, uncles: 1 }) .toArray() - hashrates[period] = this.hashrateCalculator.hashrates(blocks) + hashrates[period] = this.hashrateCalculator.hashrates(blocks, timeSpan) } return hashrates @@ -178,11 +178,11 @@ export class ExtendedStats extends DataCollectorItem { const end = block.timestamp for (const period of Object.keys(PERIODS)) { - const timeLimit = PERIODS[period].timeLimit - const start = end - timeLimit + const timeSpan = PERIODS[period].timeSpan + 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, cumulativeDifficulty: 1, uncles: 1 }) .toArray() difficulties[period] = this.difficultyCalculator.difficulties(blocks, start, end, DIFFICULTY_BUCKET_SIZE) diff --git a/src/api/modules/index.js b/src/api/modules/index.js index 752d6be0..92a94658 100644 --- a/src/api/modules/index.js +++ b/src/api/modules/index.js @@ -6,11 +6,11 @@ import { Token } from './Token' import { TxPending } from './TxPending' import { Stats } from './Stats' import { Summary } from './Summary' -// import { ExtendedStats } from './ExtendedStats' +import { ExtendedStats } from './ExtendedStats' import { ContractVerification } from './ContractVerification' import { getModulesNames, getEnabledModules } from '../lib/apiTools' -const apiModules = { Block, Tx, Address, Event, Token, TxPending, Stats, Summary, ContractVerification } +const apiModules = { ExtendedStats, Block, Tx, Address, Event, Token, TxPending, Stats, Summary, ContractVerification } export const getEnabledApiModules = modules => { const enabled = getModulesNames(getEnabledModules(modules)) diff --git a/test/services/difficultyCalculator.js b/test/services/difficultyCalculator.js index 500382db..b7f4a4b1 100644 --- a/test/services/difficultyCalculator.js +++ b/test/services/difficultyCalculator.js @@ -5,7 +5,7 @@ import { DifficultyCalculator } from '../../src/api/lib/difficultyCalculator.js' describe('difficultyCalculator', () => { const minutes = (m) => m * 60 * 1000 - const exa = (n) => new BigNumber(`${n}e50`) + const exa = (n) => new BigNumber(`${n}e18`) const epoch = (dateString) => +new Date(dateString) context('difficulties, 10 minute buckets', () => { diff --git a/test/services/hashrateCalculator.js b/test/services/hashrateCalculator.js index 379383a3..fe427330 100644 --- a/test/services/hashrateCalculator.js +++ b/test/services/hashrateCalculator.js @@ -3,9 +3,10 @@ import { BigNumber } from 'bignumber.js' import { HashrateCalculator } from '../../src/api/lib/hashrateCalculator.js' describe('hashrateCalculator', () => { - const exa = (n) => new BigNumber(`${n}e50`) + 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() @@ -21,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, cumulativeDifficulty', () => { it('returns 1 for only one miner and 1 block', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -39,8 +60,8 @@ describe('hashrateCalculator', () => { const calc = new HashrateCalculator() const blocks = [ - { miner: '0x0a', difficulty: '0x11f36beaf6690ac7e6' }, - { miner: '0x0b', difficulty: '0x11f36beaf6690ac7e6' } + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cumulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -54,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', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e3' }, + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e2' }, + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e1' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -70,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', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0c', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0d', cumulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -89,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', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cumulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0c', cumulativeDifficulty: '0x0f54a7810c212d7df0' }, + { miner: '0x0d', cumulativeDifficulty: '0x0f54a7810c212d7df0' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -108,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', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0b', cumulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0a', cumulativeDifficulty: '0x13b768fb4f683f5fd5' }, + { miner: '0x0c', cumulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0d', cumulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0d', cumulativeDifficulty: '0x0f54a7810c212d7df0' }, + { miner: '0x0a', cumulativeDifficulty: '0x11f36beaf6690ac7e6' }, + { miner: '0x0d', cumulativeDifficulty: '0x0f54a7810c212d7d00' }, + { miner: '0x0c', cumulativeDifficulty: '0x10050a0cc225820cdd' }, + { miner: '0x0b', cumulativeDifficulty: '0x10050a0cc225820fff' }, + { miner: '0x0d', cumulativeDifficulty: '0x11f36beaf6690ac7e6' } ] const hashrate = calc.hashratePercentagePerMiner(blocks) @@ -131,32 +152,16 @@ describe('hashrateCalculator', () => { }) }) - context('hashratePerMiner', () => { + context('hashratePerMiner, cumulativeDifficulty', () => { const START = 1 - it('returns an empty object when argument is not an array', () => { - const calc = new HashrateCalculator() - - const hashrate = calc.hashratePerMiner() - - assert.deepEqual(hashrate, {}) - }) - - it('returns an empty object when no blocks', () => { - const calc = new HashrateCalculator() - - const hashrate = calc.hashratePerMiner([]) - - 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', cumulativeDifficulty: exa(1), timestamp: START } ] - const hashrate = calc.hashratePerMiner(blocks) + const hashrate = calc.hashratePerMiner(blocks, 0) assert.deepEqual(hashrate, { '0x0a': '1.000 EHs' @@ -167,13 +172,13 @@ 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', cumulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0a', cumulativeDifficulty: exa(1), timestamp: START + 1 }, + { miner: '0x0a', cumulativeDifficulty: exa(1), timestamp: START + 2 }, + { miner: '0x0a', cumulativeDifficulty: exa(1), timestamp: START + 3 }, + { miner: '0x0a', cumulativeDifficulty: exa(1), timestamp: START + 4 } ] - const hashrate = calc.hashratePerMiner(blocks) + const hashrate = calc.hashratePerMiner(blocks, (START + 4) - START) assert.deepEqual(hashrate, { '0x0a': '1.250 EHs' @@ -184,19 +189,19 @@ 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', cumulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0b', cumulativeDifficulty: exa(2), timestamp: START + 1 }, + { miner: '0x0a', cumulativeDifficulty: exa(3), timestamp: START + 2 }, + { miner: '0x0c', cumulativeDifficulty: exa(4), timestamp: START + 3 }, + { miner: '0x0d', cumulativeDifficulty: exa(5), timestamp: START + 4 }, + { miner: '0x0a', cumulativeDifficulty: exa(6), timestamp: START + 5 }, + { miner: '0x0a', cumulativeDifficulty: exa(7), timestamp: START + 6 }, + { miner: '0x0b', cumulativeDifficulty: exa(8), timestamp: START + 7 }, + { miner: '0x0a', cumulativeDifficulty: exa(9), timestamp: START + 8 }, + { miner: '0x0c', cumulativeDifficulty: exa(10), timestamp: START + 9 }, + { miner: '0x0c', cumulativeDifficulty: exa(11), timestamp: START + 10 } ] - const hashrate = calc.hashratePerMiner(blocks) + const hashrate = calc.hashratePerMiner(blocks, (START + 10) - START) assert.deepEqual(hashrate, { '0x0a': '2.600 EHs', // 26 @@ -214,19 +219,19 @@ 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', cumulativeDifficulty: exa(1), timestamp: START }, + { miner: '0x0b', cumulativeDifficulty: exa(2), timestamp: START + 1 }, + { miner: '0x0a', cumulativeDifficulty: exa(3), timestamp: START + 2 }, + { miner: '0x0c', cumulativeDifficulty: exa(4), timestamp: START + 3 }, + { miner: '0x0d', cumulativeDifficulty: exa(5), timestamp: START + 4 }, + { miner: '0x0a', cumulativeDifficulty: exa(6), timestamp: START + 5 }, + { miner: '0x0a', cumulativeDifficulty: exa(7), timestamp: START + 6 }, + { miner: '0x0b', cumulativeDifficulty: exa(8), timestamp: START + 7 }, + { miner: '0x0a', cumulativeDifficulty: exa(9), timestamp: START + 8 }, + { miner: '0x0c', cumulativeDifficulty: exa(10), timestamp: START + 9 }, + { miner: '0x0c', cumulativeDifficulty: exa(11), timestamp: START + 10 } ] - const hashrate = calc.hashrates(blocks) + const hashrate = calc.hashrates(blocks, (START + 10) - START) assert.deepEqual(hashrate, { '0x0a': { avg: '2.600 EHs', perc: 0.394 }, // 26 @@ -235,5 +240,202 @@ describe('hashrateCalculator', () => { '0x0d': { avg: '0.500 EHs', perc: 0.076 } // 5 }) }) + + it('returns the cumulative diff divided the time elapsed between first and last block for multiple miners' + + 'when the difficulties are hexadecimal strings', () => { + const calc = new HashrateCalculator() + + const blocks = [ + { miner: '0x0a', cumulativeDifficulty: hexExa(1), timestamp: START }, + { miner: '0x0b', cumulativeDifficulty: hexExa(2), timestamp: START + 1 }, + { miner: '0x0a', cumulativeDifficulty: hexExa(3), timestamp: START + 2 }, + { miner: '0x0c', cumulativeDifficulty: hexExa(4), timestamp: START + 3 }, + { miner: '0x0d', cumulativeDifficulty: hexExa(5), timestamp: START + 4 }, + { miner: '0x0a', cumulativeDifficulty: hexExa(6), timestamp: START + 5 }, + { miner: '0x0a', cumulativeDifficulty: hexExa(7), timestamp: START + 6 }, + { miner: '0x0b', cumulativeDifficulty: hexExa(8), timestamp: START + 7 }, + { miner: '0x0a', cumulativeDifficulty: hexExa(9), timestamp: START + 8 }, + { miner: '0x0c', cumulativeDifficulty: hexExa(10), timestamp: START + 9 }, + { miner: '0x0c', cumulativeDifficulty: hexExa(11), timestamp: START + 10 } + ] + const hashrate = calc.hashrates(blocks, (START + 10) - START) + + assert.deepEqual(hashrate, { + '0x0a': { avg: '2.600 EHs', perc: 0.394 }, // 26 + '0x0b': { avg: '1.000 EHs', perc: 0.152 }, // 10 + '0x0c': { avg: '2.500 EHs', perc: 0.379 }, // 25 + '0x0d': { avg: '0.500 EHs', perc: 0.076 } // 5 + }) + }) + }) + + 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, cumulativeDifficulty', () => { + 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' + }) + }) }) })