From b4e4934b85447ca566177ccd1362f86546a56ac5 Mon Sep 17 00:00:00 2001 From: Shaswat Gupta <63920595+masterchief164@users.noreply.github.com> Date: Sat, 3 Dec 2022 19:48:52 +0530 Subject: [PATCH 1/2] Fix: updated the sync progress function --- lib/blockchain/chain.js | 10 ++++++---- lib/protocol/network.js | 1 + lib/protocol/networks.js | 24 ++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 7bc2fab2e..49338d1f5 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -2034,10 +2034,12 @@ class Chain extends AsyncEmitter { */ getProgress() { - const start = this.network.genesis.time; - const current = this.tip.time - start; - const end = util.now() - start - 40 * 60; - return Math.min(1, current / end); + const time = util.now(); + const rate = this.network.txnData.rate; + const lastTime = this.network.txnData.time; + const current = this.db.state.tx; + const end = this.network.txnData.count + (time - lastTime) * rate; + return Math.min(current / end, 1.0); } /** diff --git a/lib/protocol/network.js b/lib/protocol/network.js index 27d8f6061..6dad276c1 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -36,6 +36,7 @@ class Network { this.port = options.port; this.checkpointMap = options.checkpointMap; this.lastCheckpoint = options.lastCheckpoint; + this.txnData = options.txnData; this.checkpoints = []; this.halvingInterval = options.halvingInterval; this.genesis = options.genesis; diff --git a/lib/protocol/networks.js b/lib/protocol/networks.js index 235627ccf..16e6bedf7 100644 --- a/lib/protocol/networks.js +++ b/lib/protocol/networks.js @@ -127,6 +127,12 @@ main.checkpointMap = { main.lastCheckpoint = 525000; +main.txnData = { + rate: 2.925802860942233, + time: 1661697692, + count: 760120522 +}; + /** * @const {Number} * @default @@ -559,6 +565,12 @@ testnet.checkpointMap = { testnet.lastCheckpoint = 1050000; +testnet.txnData = { + rate: 0.1079119341520164, + time: 1661705221, + count: 63531852 +}; + testnet.halvingInterval = 210000; testnet.genesis = { @@ -722,6 +734,12 @@ regtest.port = 48444; regtest.checkpointMap = {}; regtest.lastCheckpoint = 0; +regtest.txnData = { + rate: 0, + time: 0, + count: 0 +}; + regtest.halvingInterval = 150; regtest.genesis = { @@ -885,6 +903,12 @@ simnet.checkpointMap = {}; simnet.lastCheckpoint = 0; +simnet.txnData = { + time: 0, + count: 0, + rate: 0 +}; + simnet.halvingInterval = 210000; simnet.genesis = { From f271412fed9b0e81541b86dd646adb309741befd Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 24 Jan 2023 21:18:08 -0500 Subject: [PATCH 2/2] chain: estimate progress after data point --- lib/blockchain/chain.js | 18 ++- test/chain-progress-test.js | 225 ++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 test/chain-progress-test.js diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 49338d1f5..554542d93 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -2035,11 +2035,21 @@ class Chain extends AsyncEmitter { getProgress() { const time = util.now(); - const rate = this.network.txnData.rate; + + const currentCount = this.db.state.tx; + const currentTime = this.tip.time; + + const lastCount = this.network.txnData.count; const lastTime = this.network.txnData.time; - const current = this.db.state.tx; - const end = this.network.txnData.count + (time - lastTime) * rate; - return Math.min(current / end, 1.0); + const rate = this.network.txnData.rate; + + let total; + if (currentCount <= lastCount) + total = lastCount + (time - lastTime) * rate; + else + total = currentCount + (time - currentTime) * rate; + + return Math.min(currentCount / total, 1); } /** diff --git a/test/chain-progress-test.js b/test/chain-progress-test.js new file mode 100644 index 000000000..456267105 --- /dev/null +++ b/test/chain-progress-test.js @@ -0,0 +1,225 @@ +/* eslint-env mocha */ +/* eslint prefer-arrow-callback: "off" */ + +'use strict'; + +const assert = require('bsert'); +const Network = require('../lib/protocol/network'); +const consensus = require('../lib/protocol/consensus'); +const Chain = require('../lib/blockchain/chain'); +const BlockStore = require('../lib/blockstore/level'); +const Miner = require('../lib/mining/miner'); +const util = require('../lib/utils/util'); +const MemWallet = require('./util/memwallet'); + +const network = Network.get('regtest'); +const savedBip16Time = consensus.BIP16_TIME; +const savedUtilNow = util.now; + +const blocks = new BlockStore({ + memory: true, + network +}); + +const chain = new Chain({ + memory: true, + blocks, + network +}); + +const miner = new Miner({ + chain +}); + +const wallet = new MemWallet({ + network, + witness: true +}); + +const address = wallet.getReceive(); + +async function mineBlocks(n) { + const entries = []; + for (let i = 0; i < n; i++) { + const job = await miner.cpu.createJob(); + // Mine blocks all ten minutes apart from regtest genesis + job.attempt.time = chain.tip.time + (60 * 10); + const block = await job.mineAsync(); + const entry = await chain.add(block); + wallet.addBlock(entry, block.txs); + entries.push(entry); + } + return entries; +} + +async function mineBlockWithMTXs(mtxs) { + const job = await miner.cpu.createJob(); + for (const mtx of mtxs) + job.addTX(mtx.toTX(), mtx.view); + job.refresh(); + job.attempt.time = chain.tip.time + (60 * 10); + const block = await job.mineAsync(); + const entry = await chain.add(block); + wallet.addBlock(entry, block.txs); + return entry; +} + +describe('Chain Sync Progress', function () { + before(async () => { + await blocks.open(); + await chain.open(); + await miner.open(); + + miner.addresses.length = 0; + miner.addAddress(address); + + // regtest genesis timestamp ordinarily pre-dates P2SH + consensus.BIP16_TIME = 0; + }); + + after(async () => { + await miner.close(); + await chain.close(); + await blocks.close(); + + // restore + consensus.BIP16_TIME = savedBip16Time; + util.now = savedUtilNow; + network.txnData = { + rate: 0, + time: 0, + count: 0 + }; + }); + + it('should generate 100 blocks with 1 tx each (coinbase only)', async () => { + await mineBlocks(100); + + // Imagine releasing software at this point using these data + network.txnData = { + count: 101, + time: chain.tip.time, + rate: 1 / 600 // tx per second (one per ten minute block) + }; + }); + + it('should generate 100 blocks with 2 tx each', async () => { + for (let i = 0; i < 100; i++) { + const mtx = await wallet.create({ + outputs: [{ + address: wallet.getAddress(), + value: 1e8 + }] + }); + await mineBlockWithMTXs([mtx]); + } + }); + + it('should generate 100 blocks with 3 tx each', async () => { + for (let i = 0; i < 100; i++) { + const mtx1 = await wallet.create({ + outputs: [{ + address: wallet.getAddress(), + value: 1e8 + }] + }); + // prevent double spend + wallet.addTX(mtx1.toTX()); + const mtx2 = await wallet.create({ + outputs: [{ + address: wallet.getAddress(), + value: 1e8 + }] + }); + await mineBlockWithMTXs([mtx1, mtx2]); + } + }); + + it('should have expected chain state', async () => { + assert.strictEqual(chain.height, 300); + assert.strictEqual(chain.db.state.tx, (1 + 100 + 200 + 300)); + }); + + describe('New chain', function () { + // time never changes + util.now = () => { + return chain.tip.time; + }; + + const newBlocks = new BlockStore({ + memory: true, + network + }); + + const newChain = new Chain({ + memory: true, + blocks, + network + }); + + before(async () => { + await newBlocks.open(); + await newChain.open(); + }); + + after(async () => { + await newChain.close(); + await newBlocks.close(); + }); + + it('should sync the first 100 blocks and get progress', async () => { + for (let i = 1; i <= 100; i++) { + const entry = await chain.getEntry(i); + const block = await chain.getBlock(entry.hash); + await newChain.add(block); + } + + const percent = parseInt(newChain.getProgress() * 100); + // Only 100 out of 600 total txs have been processed + // but at this point all we know about the chain is the + // hard-coded values. We assume the tx rate of one per ten minutes + // continues until the current time, which turns out to be wrong. + // The current guess is 100 down out of (we think) 300 total. + assert.strictEqual(percent, 33); + }); + + it('should sync the next 100 blocks and get progress', async () => { + for (let i = 101; i <= 200; i++) { + const entry = await chain.getEntry(i); + const block = await chain.getBlock(entry.hash); + await newChain.add(block); + } + + const percent = parseInt(newChain.getProgress() * 100); + // Even though we have observed the tx rate on chain double + // over the last 100 blocks, we continue to use the 1 tx per ten minutes + // rate to predict the future from this point forward. + // The new guess is 300 down out of (we think) 400 total. + assert.strictEqual(percent, 75); + }); + + it('should sync the next 99 blocks and approach 100%', async () => { + for (let i = 201; i < 300; i++) { + const entry = await chain.getEntry(i); + const block = await chain.getBlock(entry.hash); + await newChain.add(block); + } + + const percent = parseInt(newChain.getProgress() * 100); + // As we approach the current time the actual tx count gets closer and + // closer to accurate and the amount of future txs we need to predict + // drops to almost zero. + // The new guess is essentially 599 down out of (we think) 600 total. + assert.strictEqual(percent, 99); + }); + + it('should sync the last block and reach 100%', async () => { + const entry = await chain.getEntry(300); + const block = await chain.getBlock(entry.hash); + await newChain.add(block); + + const percent = parseInt(newChain.getProgress() * 100); + assert.strictEqual(percent, 100); + }); + }); +});