From ff41a3503981036c088fa4c110f1e1c7e3ae8ab6 Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Thu, 21 Mar 2024 17:57:34 -0700 Subject: [PATCH 01/40] Add updateRoot method to fetchers --- packages/client/src/sync/fetcher/accountfetcher.ts | 5 +++++ packages/client/src/sync/fetcher/storagefetcher.ts | 4 ++++ packages/client/src/sync/fetcher/trienodefetcher.ts | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 79c9c48adc..ca0e0b1ba4 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -580,6 +580,11 @@ export class AccountFetcher extends Fetcher updateStateRoot(stateRoot: Uint8Array) { this.root = stateRoot + + this.storageFetcher.updateStateRoot(stateRoot) + + // TODO trieNodeFetcher needs to be constantly healing while other fetchers work + this.trieNodeFetcher.updateStateRoot(stateRoot) } nextTasks(): void { diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index ed173499b4..fa4807a1dc 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -612,6 +612,10 @@ export class StorageFetcher extends Fetcher return tasks } + updateStateRoot(stateRoot: Uint8Array) { + this.root = stateRoot + } + nextTasks(): void { try { if (this.in.length === 0) { From 6c00e9c60ff94873abe80f8ac85a64aea3a418f8 Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Thu, 21 Mar 2024 17:58:01 -0700 Subject: [PATCH 02/40] Update the fetcher roots in syncWithPeer if fetcher already exists --- packages/client/src/sync/snapsync.ts | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 0ab6c362ad..ad1887a41d 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -239,15 +239,22 @@ export class SnapSynchronizer extends Synchronizer { : 'previous fetcher errored=' + this.fetcher.syncErrored?.message }` ) - this.fetcher = new AccountFetcher({ - config: this.config, - pool: this.pool, - stateManager: this.execution.vm.stateManager as DefaultStateManager, - root: stateRoot, - // This needs to be determined from the current state of the MPT dump - first: BigInt(0), - fetcherDoneFlags: this.fetcherDoneFlags, - }) + + if (this.fetcher === undefined) { + this.fetcher = new AccountFetcher({ + config: this.config, + pool: this.pool, + stateManager: this.execution.vm.stateManager as DefaultStateManager, + root: stateRoot, + // This needs to be determined from the current state of the MPT dump + first: BigInt(0), + fetcherDoneFlags: this.fetcherDoneFlags, + }) + } else { + // update root + this.config.logger.info(`UPDATING ROOTS`) + this.fetcher?.updateStateRoot(stateRoot) + } } else { return false } From dd21e7309aa228f1f8decd18d40ca378006c9017 Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Thu, 4 Apr 2024 16:46:05 -0700 Subject: [PATCH 03/40] Update fetcher roots on FCUs --- packages/client/src/service/fullethereumservice.ts | 6 +++++- packages/client/src/sync/snapsync.ts | 7 ++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/client/src/service/fullethereumservice.ts b/packages/client/src/service/fullethereumservice.ts index c1521bdc73..e2eadab116 100644 --- a/packages/client/src/service/fullethereumservice.ts +++ b/packages/client/src/service/fullethereumservice.ts @@ -243,7 +243,11 @@ export class FullEthereumService extends Service { * vm execution */ async buildHeadState(): Promise { - if (this.building) return + // If during building FCU identifies a new root/height to sync to, evalueate new height with sync + // strategy and initiate state healing and fetcher root updates for snap sync + if (this.building) { + this.snapsync?.sync() + } this.building = true try { diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index ad1887a41d..f65e83b750 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -220,10 +220,7 @@ export class SnapSynchronizer extends Synchronizer { this.config.logger.info(`New sync target height=${height} hash=${bytesToHex(latest.hash())}`) } - if ( - (this.fetcher === null || this.fetcher.syncErrored !== undefined) && - this.config.syncTargetHeight <= latest.number + this.config.snapAvailabilityDepth - ) { + if (this.config.syncTargetHeight <= latest.number + this.config.snapAvailabilityDepth) { if ((this.fetcherDoneFlags.snapTargetHeight ?? BIGINT_0) < latest.number) { this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot @@ -240,7 +237,7 @@ export class SnapSynchronizer extends Synchronizer { }` ) - if (this.fetcher === undefined) { + if (this.fetcher === null || this.fetcher.syncErrored !== undefined) { this.fetcher = new AccountFetcher({ config: this.config, pool: this.pool, From 19adeea3465d62be94dedc795e5ed0c7d60c00bc Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Thu, 4 Apr 2024 23:54:29 -0700 Subject: [PATCH 04/40] Call syncWithPeer directly from buildHeadState for updating root --- packages/client/src/service/fullethereumservice.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/service/fullethereumservice.ts b/packages/client/src/service/fullethereumservice.ts index e2eadab116..eeeea114aa 100644 --- a/packages/client/src/service/fullethereumservice.ts +++ b/packages/client/src/service/fullethereumservice.ts @@ -246,7 +246,8 @@ export class FullEthereumService extends Service { // If during building FCU identifies a new root/height to sync to, evalueate new height with sync // strategy and initiate state healing and fetcher root updates for snap sync if (this.building) { - this.snapsync?.sync() + const peer = await this.snapsync?.best() + void this.snapsync?.syncWithPeer(peer) } this.building = true From 1e1b1cc8d717904d5dcc69c38fb8ac90230f1ae7 Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Tue, 9 Apr 2024 12:57:04 -0700 Subject: [PATCH 05/40] Keep track of requested pathStrings to remove duplicate requests in TrieNodeFetcher --- .../src/sync/fetcher/trienodefetcher.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index da2510c927..1a1da1afad 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -60,6 +60,7 @@ type FetchedNodeData = { } type NodeRequestData = { + requested: boolean nodeHash: string nodeParentHash: string parentAccountHash?: string // for leaf account nodes that contain a storage component @@ -116,6 +117,7 @@ export class TrieNodeFetcher extends Fetcher // will always start with root node as first set of node requests this.pathToNodeRequestData.setElement('', { + requested: false, nodeHash: bytesToHex(this.root), nodeParentHash: '', // root node does not have a parent } as NodeRequestData) @@ -256,6 +258,7 @@ export class TrieNodeFetcher extends Fetcher storagePath, ].join('/') this.pathToNodeRequestData.setElement(syncPath, { + requested: false, nodeHash: bytesToHex(storageRoot), nodeParentHash: nodeHash, parentAccountHash: nodeHash, @@ -292,6 +295,7 @@ export class TrieNodeFetcher extends Fetcher pathString ) as NodeRequestData this.pathToNodeRequestData.setElement(childNode.path, { + requested: false, nodeHash: bytesToHex(childNode.nodeHash as Uint8Array), nodeParentHash: nodeHash, // TODO root node does not have a parent, so handle that in the leaf callback when checking if dependencies are met recursively parentAccountHash, @@ -404,17 +408,25 @@ export class TrieNodeFetcher extends Fetcher if (this.pathToNodeRequestData.size() > 0) { let { pathStrings } = this.getSortedPathStrings() // TODO pass in number of paths to return while (tasks.length < maxTasks && pathStrings.length > 0) { - const requestedPathStrings = pathStrings.slice(0, max) + const pendingPathStrings = pathStrings.slice(0, max) pathStrings = pathStrings.slice(max + 1) - for (const pathString of requestedPathStrings) { - const nodeHash = this.pathToNodeRequestData.getElementByKey(pathString)?.nodeHash // TODO return node set too from sorted function and avoid lookups here - if (nodeHash === undefined) throw Error('Path should exist') - this.requestedNodeToPath.set(nodeHash as unknown as string, pathString) + const neededPathStrings = [] + for (const pathString of pendingPathStrings) { + const requestData = this.pathToNodeRequestData.getElementByKey(pathString) // TODO return node set too from sorted function and avoid lookups here + if (requestData === undefined) throw Error('Path should exist') + if (requestData.requested === false) { + this.requestedNodeToPath.set(requestData.nodeHash as unknown as string, pathString) + this.pathToNodeRequestData.setElement(pathString, { + ...requestData, + requested: true, + }) + neededPathStrings.push(pathString) + } } this.debug('At start of mergeAndFormatPaths') - const paths = mergeAndFormatKeyPaths(requestedPathStrings) as unknown as Uint8Array[][] + const paths = mergeAndFormatKeyPaths(neededPathStrings) as unknown as Uint8Array[][] tasks.push({ - pathStrings: requestedPathStrings, + pathStrings: neededPathStrings, paths, }) this.debug(`Created new tasks num=${tasks.length}`) From 8077f4dd43168aa36cc4738ba05b992dc6e0c5bd Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Tue, 9 Apr 2024 18:35:17 -0700 Subject: [PATCH 06/40] Fix test --- packages/client/test/sync/fetcher/trienodefetcher.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/test/sync/fetcher/trienodefetcher.spec.ts b/packages/client/test/sync/fetcher/trienodefetcher.spec.ts index d4b1202801..f4999f6f41 100644 --- a/packages/client/test/sync/fetcher/trienodefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/trienodefetcher.spec.ts @@ -212,6 +212,7 @@ describe('[TrieNodeFetcher]', async () => { fetcher.pathToNodeRequestData = new OrderedMap() fetcher.pathToNodeRequestData.setElement('', { + requested: false, nodeHash: bytesToHex(new Uint8Array(0)), nodeParentHash: '', }) From 6a2b266545f854b88b2f7019ff4d09df0ebd23bd Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Thu, 11 Apr 2024 12:47:07 +0200 Subject: [PATCH 07/40] Client -> Peer: add lastEthStatusUpdate property to Peer, set initially on protocol binding, small local refactor --- packages/client/src/net/peer/peer.ts | 28 ++++++++++++++-------------- packages/client/src/net/peerpool.ts | 4 +++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index 8fb2bffa5e..70660d711d 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -49,6 +49,8 @@ export abstract class Peer extends EventEmitter { public snap?: BoundSnapProtocol public les?: BoundLesProtocol + public lastEthStatusUpdate?: number + /* If the peer is in the PeerPool. If true, messages are handled immediately. @@ -108,28 +110,26 @@ export abstract class Peer extends EventEmitter { if (protocol.name === 'eth') { bound = new BoundEthProtocol(boundOpts) + + await bound!.handshake(sender) + this.lastEthStatusUpdate = Date.now() + + this.eth = bound } else if (protocol.name === 'les') { bound = new BoundLesProtocol(boundOpts) - } else if (protocol.name === 'snap') { - bound = new BoundSnapProtocol(boundOpts) - } else { - throw new Error(`addProtocol: ${protocol.name} protocol not supported`) - } - // Handshake only when snap, else - if (protocol.name !== 'snap') { await bound!.handshake(sender) - } else { - if (sender.status === undefined) throw Error('Snap can only be bound on handshaked peer') - } - if (protocol.name === 'eth') { - this.eth = bound + this.les = bound } else if (protocol.name === 'snap') { + bound = new BoundSnapProtocol(boundOpts) + if (sender.status === undefined) throw Error('Snap can only be bound on handshaked peer') + this.snap = bound - } else if (protocol.name === 'les') { - this.les = bound + } else { + throw new Error(`addProtocol: ${protocol.name} protocol not supported`) } + this.boundProtocols.push(bound) } diff --git a/packages/client/src/net/peerpool.ts b/packages/client/src/net/peerpool.ts index fdc5af70d1..f792da1bb5 100644 --- a/packages/client/src/net/peerpool.ts +++ b/packages/client/src/net/peerpool.ts @@ -146,7 +146,9 @@ export class PeerPool { * @param filterFn filter function to apply before finding idle peers */ idle(filterFn = (_peer: Peer) => true): Peer | undefined { - const idle = this.peers.filter((p) => p.idle && filterFn(p)) + const idle = this.peers.filter((p) => { + return p.idle && filterFn(p) ? true : false + }) if (idle.length > 0) { const index = Math.floor(Math.random() * idle.length) return idle[index] From 4dd9bf86abca02bf7d96c894ba39d12e0e5a969e Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Thu, 11 Apr 2024 13:39:42 +0200 Subject: [PATCH 08/40] Redo handshake in client peer pool periodically, add force option to sendStatus for devp2p protocols --- packages/client/src/net/peer/peer.ts | 7 ++-- packages/client/src/net/peer/rlpxpeer.ts | 1 + packages/client/src/net/peerpool.ts | 35 ++++++++++++++++++- .../client/src/net/protocol/rlpxsender.ts | 2 +- packages/devp2p/src/protocol/eth.ts | 4 +-- packages/devp2p/src/protocol/les.ts | 4 +-- 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index 70660d711d..15b3011a11 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -3,7 +3,7 @@ import { EventEmitter } from 'events' import { BoundEthProtocol, BoundLesProtocol, BoundSnapProtocol } from '../protocol' import type { Config } from '../../config' -import type { BoundProtocol, Protocol, Sender } from '../protocol' +import type { BoundProtocol, Protocol, RlpxSender, Sender } from '../protocol' import type { Server } from '../server' export interface PeerOptions { @@ -46,11 +46,12 @@ export abstract class Peer extends EventEmitter { // TODO check if this should be moved into RlpxPeer public eth?: BoundEthProtocol + public lastEthStatusUpdate?: number + public ethSender?: RlpxSender + public snap?: BoundSnapProtocol public les?: BoundLesProtocol - public lastEthStatusUpdate?: number - /* If the peer is in the PeerPool. If true, messages are handled immediately. diff --git a/packages/client/src/net/peer/rlpxpeer.ts b/packages/client/src/net/peer/rlpxpeer.ts index 39b3469c5f..0bf06d209a 100644 --- a/packages/client/src/net/peer/rlpxpeer.ts +++ b/packages/client/src/net/peer/rlpxpeer.ts @@ -168,6 +168,7 @@ export class RlpxPeer extends Peer { // handshake, and can just use the eth handshake if (protocol && name !== 'snap') { const sender = new RlpxSender(rlpxProtocol as Devp2pETH | Devp2pLES | Devp2pSNAP) + this.ethSender = sender return this.addProtocol(sender, protocol).then(() => { if (name === 'eth') { const snapRlpxProtocol = rlpxPeer diff --git a/packages/client/src/net/peerpool.ts b/packages/client/src/net/peerpool.ts index f792da1bb5..f23712073a 100644 --- a/packages/client/src/net/peerpool.ts +++ b/packages/client/src/net/peerpool.ts @@ -1,9 +1,12 @@ import { Hardfork } from '@ethereumjs/common' +import { bytesToUnprefixedHex } from '@ethereumjs/util' import { Event } from '../types' +import { type Peer, RlpxPeer } from './peer' +import { RlpxSender } from './protocol' + import type { Config } from '../config' -import type { Peer } from './peer' export interface PeerPoolOptions { /* Config */ @@ -31,7 +34,13 @@ export class PeerPool { */ private DEFAULT_STATUS_CHECK_INTERVAL = 20000 + /** + * Default status check interval (in ms) + */ + private DEFAULT_PEER_ETH_STATUS_CHECK_INTERVAL = 5000 + private _statusCheckInterval: NodeJS.Timeout | undefined /* global NodeJS */ + private _peerEthStatusCheckInterval: NodeJS.Timeout | undefined private _reconnectTimeout: NodeJS.Timeout | undefined /** @@ -87,6 +96,12 @@ export class PeerPool { this.DEFAULT_STATUS_CHECK_INTERVAL ) + this._peerEthStatusCheckInterval = setInterval( + // eslint-disable-next-line @typescript-eslint/await-thenable + await this._peerEthStatusCheck.bind(this), + this.DEFAULT_PEER_ETH_STATUS_CHECK_INTERVAL + ) + this.running = true return true } @@ -99,6 +114,7 @@ export class PeerPool { await this.close() } clearInterval(this._statusCheckInterval as NodeJS.Timeout) + clearInterval(this._peerEthStatusCheckInterval as NodeJS.Timeout) clearTimeout(this._reconnectTimeout as NodeJS.Timeout) this.running = false return true @@ -254,4 +270,21 @@ export class PeerPool { this.noPeerPeriods = 0 } } + + /** + * Periodically check pooled peers for the last ETH status exchange + * and trigger new status msg exchanges if too old + */ + async _peerEthStatusCheck() { + for (const p of this.peers) { + if (!p.idle && p.eth !== undefined && p.ethSender !== undefined && p instanceof RlpxPeer) { + p.idle = true + console.log('here!') + console.log(bytesToUnprefixedHex(p.eth.status.bestHash)) + await p.eth.handshake(p.ethSender!) + console.log(bytesToUnprefixedHex(p.eth.status.bestHash)) + p.idle = false + } + } + } } diff --git a/packages/client/src/net/protocol/rlpxsender.ts b/packages/client/src/net/protocol/rlpxsender.ts index f7e248d61c..3c371da70f 100644 --- a/packages/client/src/net/protocol/rlpxsender.ts +++ b/packages/client/src/net/protocol/rlpxsender.ts @@ -33,7 +33,7 @@ export class RlpxSender extends Sender { */ sendStatus(status: any) { try { - this.sender.sendStatus(status) + this.sender.sendStatus(status, true) } catch (err: any) { this.emit('error', err) } diff --git a/packages/devp2p/src/protocol/eth.ts b/packages/devp2p/src/protocol/eth.ts index 5772808ad2..33eb08a6a4 100644 --- a/packages/devp2p/src/protocol/eth.ts +++ b/packages/devp2p/src/protocol/eth.ts @@ -271,8 +271,8 @@ export class ETH extends Protocol { return sStr } - sendStatus(status: ETH.StatusOpts) { - if (this._status !== null) return + sendStatus(status: ETH.StatusOpts, force = false) { + if (this._status !== null && !force) return this._status = [ intToBytes(this._version), bigIntToBytes(this._peer.common.chainId()), diff --git a/packages/devp2p/src/protocol/les.ts b/packages/devp2p/src/protocol/les.ts index bb38eff6c6..18f746acf7 100644 --- a/packages/devp2p/src/protocol/les.ts +++ b/packages/devp2p/src/protocol/les.ts @@ -182,8 +182,8 @@ export class LES extends Protocol { return sStr } - sendStatus(status: LES.Status) { - if (this._status !== null) return + sendStatus(status: LES.Status, force = false) { + if (this._status !== null && !force) return if (status.announceType === undefined) { status['announceType'] = intToBytes(DEFAULT_ANNOUNCE_TYPE) From e268f9ac4e7d3982ed1c97129ffab3fa300cf897 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 11:25:29 +0200 Subject: [PATCH 09/40] Some basic refinement of peer pool periodic request logic (refactor preparation) --- packages/client/src/net/peer/peer.ts | 3 +-- packages/client/src/net/peer/rlpxpeer.ts | 1 - packages/client/src/net/peerpool.ts | 17 ++++++++--------- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index 15b3011a11..07e2e5bb72 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -3,7 +3,7 @@ import { EventEmitter } from 'events' import { BoundEthProtocol, BoundLesProtocol, BoundSnapProtocol } from '../protocol' import type { Config } from '../../config' -import type { BoundProtocol, Protocol, RlpxSender, Sender } from '../protocol' +import type { BoundProtocol, Protocol, Sender } from '../protocol' import type { Server } from '../server' export interface PeerOptions { @@ -47,7 +47,6 @@ export abstract class Peer extends EventEmitter { // TODO check if this should be moved into RlpxPeer public eth?: BoundEthProtocol public lastEthStatusUpdate?: number - public ethSender?: RlpxSender public snap?: BoundSnapProtocol public les?: BoundLesProtocol diff --git a/packages/client/src/net/peer/rlpxpeer.ts b/packages/client/src/net/peer/rlpxpeer.ts index 0bf06d209a..39b3469c5f 100644 --- a/packages/client/src/net/peer/rlpxpeer.ts +++ b/packages/client/src/net/peer/rlpxpeer.ts @@ -168,7 +168,6 @@ export class RlpxPeer extends Peer { // handshake, and can just use the eth handshake if (protocol && name !== 'snap') { const sender = new RlpxSender(rlpxProtocol as Devp2pETH | Devp2pLES | Devp2pSNAP) - this.ethSender = sender return this.addProtocol(sender, protocol).then(() => { if (name === 'eth') { const snapRlpxProtocol = rlpxPeer diff --git a/packages/client/src/net/peerpool.ts b/packages/client/src/net/peerpool.ts index f23712073a..bf052e7db3 100644 --- a/packages/client/src/net/peerpool.ts +++ b/packages/client/src/net/peerpool.ts @@ -4,7 +4,6 @@ import { bytesToUnprefixedHex } from '@ethereumjs/util' import { Event } from '../types' import { type Peer, RlpxPeer } from './peer' -import { RlpxSender } from './protocol' import type { Config } from '../config' @@ -98,7 +97,7 @@ export class PeerPool { this._peerEthStatusCheckInterval = setInterval( // eslint-disable-next-line @typescript-eslint/await-thenable - await this._peerEthStatusCheck.bind(this), + await this._peerBestBlockUpdate.bind(this), this.DEFAULT_PEER_ETH_STATUS_CHECK_INTERVAL ) @@ -272,16 +271,16 @@ export class PeerPool { } /** - * Periodically check pooled peers for the last ETH status exchange - * and trigger new status msg exchanges if too old + * Periodically check for for the latest best block known by a + * peer to allow for a more accurate best peer choice */ - async _peerEthStatusCheck() { + async _peerBestBlockUpdate() { for (const p of this.peers) { - if (!p.idle && p.eth !== undefined && p.ethSender !== undefined && p instanceof RlpxPeer) { + if (!p.idle && p.eth !== undefined && p instanceof RlpxPeer) { p.idle = true - console.log('here!') - console.log(bytesToUnprefixedHex(p.eth.status.bestHash)) - await p.eth.handshake(p.ethSender!) + console.log(this.config.syncTargetHeight) + //const block = await p.eth.getBlockHeaders() + //console.log(block) console.log(bytesToUnprefixedHex(p.eth.status.bestHash)) p.idle = false } From b0532774e6a7d7ed6ae41315cff21a4a9241a2bb Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 11:29:57 +0200 Subject: [PATCH 10/40] Client: consolidate latest() method in base Synchronizer class --- packages/client/src/sync/beaconsync.ts | 13 +------------ packages/client/src/sync/fullsync.ts | 11 ----------- packages/client/src/sync/lightsync.ts | 11 ----------- packages/client/src/sync/snapsync.ts | 14 -------------- packages/client/src/sync/sync.ts | 12 ++++++++++++ 5 files changed, 13 insertions(+), 48 deletions(-) diff --git a/packages/client/src/sync/beaconsync.ts b/packages/client/src/sync/beaconsync.ts index 6732c50d19..15c4869d09 100644 --- a/packages/client/src/sync/beaconsync.ts +++ b/packages/client/src/sync/beaconsync.ts @@ -116,7 +116,7 @@ export class BeaconSynchronizer extends Synchronizer { if (peers.length < this.config.minPeers && !this.forceSync) return for (const peer of peers) { const latest = await this.latest(peer) - if (latest) { + if (latest !== undefined) { const { number } = latest if (!best || best[1] < number) { best = [peer, number] @@ -126,17 +126,6 @@ export class BeaconSynchronizer extends Synchronizer { return best ? best[0] : undefined } - /** - * Get latest header of peer - */ - async latest(peer: Peer) { - const result = await peer.eth?.getBlockHeaders({ - block: peer.eth!.status.bestHash, - max: 1, - }) - return result ? result[1][0] : undefined - } - /** * Start synchronizer. * If passed a block, will initialize sync starting from the block. diff --git a/packages/client/src/sync/fullsync.ts b/packages/client/src/sync/fullsync.ts index a82e631709..2f3427a7d2 100644 --- a/packages/client/src/sync/fullsync.ts +++ b/packages/client/src/sync/fullsync.ts @@ -138,17 +138,6 @@ export class FullSynchronizer extends Synchronizer { return best } - /** - * Get latest header of peer - */ - async latest(peer: Peer) { - const result = await peer.eth?.getBlockHeaders({ - block: peer.eth!.status.bestHash, - max: 1, - }) - return result ? result[1][0] : undefined - } - /** * Checks if tx pool should be started */ diff --git a/packages/client/src/sync/lightsync.ts b/packages/client/src/sync/lightsync.ts index 411d433110..985cb19c93 100644 --- a/packages/client/src/sync/lightsync.ts +++ b/packages/client/src/sync/lightsync.ts @@ -84,17 +84,6 @@ export class LightSynchronizer extends Synchronizer { return best } - /** - * Get latest header of peer - */ - async latest(peer: Peer) { - const result = await peer.les?.getBlockHeaders({ - block: peer.les!.status.headHash, - max: 1, - }) - return result?.headers[0] - } - /** * Called from `sync()` to sync headers and state from peer starting from current height. * @param peer remote peer to sync with diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 0ab6c362ad..3d4e415f9a 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -99,20 +99,6 @@ export class SnapSynchronizer extends Synchronizer { return best ? best[0] : undefined } - /** - * Get latest header of peer - */ - async latest(peer: Peer) { - // TODO: refine the way to query latest to fetch for the peer - const blockHash = peer.eth!.status.bestHash - // const blockHash = this.skeleton?.headHash() ?? peer.eth!.status.bestHash - const result = await peer.eth?.getBlockHeaders({ - block: blockHash, - max: 1, - }) - return result ? result[1][0] : undefined - } - /** * Start synchronizer. */ diff --git a/packages/client/src/sync/sync.ts b/packages/client/src/sync/sync.ts index 3ee493986f..a7da72ebb0 100644 --- a/packages/client/src/sync/sync.ts +++ b/packages/client/src/sync/sync.ts @@ -10,6 +10,7 @@ import type { Config } from '../config' import type { Peer } from '../net/peer/peer' import type { PeerPool } from '../net/peerpool' import type { AccountFetcher, BlockFetcher, HeaderFetcher, ReverseBlockFetcher } from './fetcher' +import type { BlockHeader } from '@ethereumjs/block' export interface SynchronizerOptions { /* Config */ @@ -163,6 +164,17 @@ export abstract class Synchronizer { } } + /** + * Get latest header of peer + */ + async latest(peer: Peer): Promise { + const result = await peer.eth?.getBlockHeaders({ + block: peer.eth!.status.bestHash, + max: 1, + }) + return result ? result[1][0] : undefined + } + /** * Fetch all blocks from current height up to highest found amongst peers * @returns when sync is completed From 72a28d15786a027c52fde2b84431fe832041b671 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 11:54:47 +0200 Subject: [PATCH 11/40] Client: add preparatory updatedBestHeader to Peer, refactor latest() to move from Sync -> Peer --- packages/client/src/net/peer/peer.ts | 20 ++++++++++++++++--- .../client/src/net/protocol/boundprotocol.ts | 9 +++++++++ packages/client/src/rpc/modules/eth.ts | 2 +- packages/client/src/sync/beaconsync.ts | 4 ++-- packages/client/src/sync/fullsync.ts | 2 +- packages/client/src/sync/lightsync.ts | 2 +- packages/client/src/sync/snapsync.ts | 6 +++--- packages/client/src/sync/sync.ts | 12 ----------- 8 files changed, 34 insertions(+), 23 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index 07e2e5bb72..d6d696ccfa 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -5,6 +5,7 @@ import { BoundEthProtocol, BoundLesProtocol, BoundSnapProtocol } from '../protoc import type { Config } from '../../config' import type { BoundProtocol, Protocol, Sender } from '../protocol' import type { Server } from '../server' +import type { BlockHeader } from '@ethereumjs/block' export interface PeerOptions { /* Config */ @@ -46,8 +47,6 @@ export abstract class Peer extends EventEmitter { // TODO check if this should be moved into RlpxPeer public eth?: BoundEthProtocol - public lastEthStatusUpdate?: number - public snap?: BoundSnapProtocol public les?: BoundLesProtocol @@ -92,6 +91,22 @@ export abstract class Peer extends EventEmitter { abstract connect(): Promise + /** + * Get latest header of peer + */ + async latest(): Promise { + const result = await this.eth?.getBlockHeaders({ + block: this.eth!.status.bestHash, + max: 1, + }) + if (result !== undefined) { + const header = result[1][0] + this.eth!.updatedBestHeader = header + return header + } + return + } + /** * Handle unhandled messages along handshake */ @@ -112,7 +127,6 @@ export abstract class Peer extends EventEmitter { bound = new BoundEthProtocol(boundOpts) await bound!.handshake(sender) - this.lastEthStatusUpdate = Date.now() this.eth = bound } else if (protocol.name === 'les') { diff --git a/packages/client/src/net/protocol/boundprotocol.ts b/packages/client/src/net/protocol/boundprotocol.ts index 14e56c1072..74cb3bd465 100644 --- a/packages/client/src/net/protocol/boundprotocol.ts +++ b/packages/client/src/net/protocol/boundprotocol.ts @@ -43,6 +43,15 @@ export class BoundProtocol { private resolvers: Map private messageQueue: Message[] = [] + /** + * An eventual updated best head. + * + * If set this is by design known to be greater or equal the block hash from + * the initial `STATUS` exchange (`_status` property here) and `updatedBestHash` + * number/hash should take precedence. + */ + public updatedBestHeader?: BlockHeader + /** * Create bound protocol */ diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index d1477a97c6..e32f89fc3f 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -1118,7 +1118,7 @@ export class Eth { message: `no peer available for synchronization`, } } - const highestBlockHeader = await synchronizer.latest(bestPeer) + const highestBlockHeader = await bestPeer.latest() if (!highestBlockHeader) { throw { code: INTERNAL_ERROR, diff --git a/packages/client/src/sync/beaconsync.ts b/packages/client/src/sync/beaconsync.ts index 15c4869d09..30fe54b24d 100644 --- a/packages/client/src/sync/beaconsync.ts +++ b/packages/client/src/sync/beaconsync.ts @@ -115,7 +115,7 @@ export class BeaconSynchronizer extends Synchronizer { const peers = this.pool.peers.filter(this.syncable.bind(this)) if (peers.length < this.config.minPeers && !this.forceSync) return for (const peer of peers) { - const latest = await this.latest(peer) + const latest = await peer.latest() if (latest !== undefined) { const { number } = latest if (!best || best[1] < number) { @@ -210,7 +210,7 @@ export class BeaconSynchronizer extends Synchronizer { return false } - const latest = peer ? await this.latest(peer) : undefined + const latest = peer ? await peer.latest() : undefined if (!latest) return false const height = latest.number diff --git a/packages/client/src/sync/fullsync.ts b/packages/client/src/sync/fullsync.ts index 2f3427a7d2..470aa1569b 100644 --- a/packages/client/src/sync/fullsync.ts +++ b/packages/client/src/sync/fullsync.ts @@ -164,7 +164,7 @@ export class FullSynchronizer extends Synchronizer { * @returns a boolean if the setup was successful */ async syncWithPeer(peer?: Peer): Promise { - const latest = peer ? await this.latest(peer) : undefined + const latest = peer ? await peer.latest() : undefined if (!latest) return false const height = latest.number diff --git a/packages/client/src/sync/lightsync.ts b/packages/client/src/sync/lightsync.ts index 985cb19c93..2c33a19aa4 100644 --- a/packages/client/src/sync/lightsync.ts +++ b/packages/client/src/sync/lightsync.ts @@ -90,7 +90,7 @@ export class LightSynchronizer extends Synchronizer { * @returns a boolean if the setup was successful */ async syncWithPeer(peer?: Peer): Promise { - const latest = peer ? await this.latest(peer) : undefined + const latest = peer ? await peer.latest() : undefined if (!latest) return false const height = peer!.les!.status.headNum diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 3d4e415f9a..bd3657eade 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -84,8 +84,8 @@ export class SnapSynchronizer extends Synchronizer { const peers = this.pool.peers.filter(this.syncable.bind(this)) if (peers.length < this.config.minPeers && !this.forceSync) return for (const peer of peers) { - const latest = await this.latest(peer) - if (latest) { + const latest = await peer.latest() + if (latest !== undefined) { const { number } = latest if ( (!best && @@ -192,7 +192,7 @@ export class SnapSynchronizer extends Synchronizer { return false } - const latest = peer ? await this.latest(peer) : undefined + const latest = peer ? await peer.latest() : undefined if (!latest) { return false } diff --git a/packages/client/src/sync/sync.ts b/packages/client/src/sync/sync.ts index a7da72ebb0..3ee493986f 100644 --- a/packages/client/src/sync/sync.ts +++ b/packages/client/src/sync/sync.ts @@ -10,7 +10,6 @@ import type { Config } from '../config' import type { Peer } from '../net/peer/peer' import type { PeerPool } from '../net/peerpool' import type { AccountFetcher, BlockFetcher, HeaderFetcher, ReverseBlockFetcher } from './fetcher' -import type { BlockHeader } from '@ethereumjs/block' export interface SynchronizerOptions { /* Config */ @@ -164,17 +163,6 @@ export abstract class Synchronizer { } } - /** - * Get latest header of peer - */ - async latest(peer: Peer): Promise { - const result = await peer.eth?.getBlockHeaders({ - block: peer.eth!.status.bestHash, - max: 1, - }) - return result ? result[1][0] : undefined - } - /** * Fetch all blocks from current height up to highest found amongst peers * @returns when sync is completed From f29c3c6cd33ca6f8aedf870d82499ae3a6c7df20 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 15:50:14 +0200 Subject: [PATCH 12/40] Client: add potential best header num differentiated logic to Peer, latest() call on peers in peer pool --- packages/client/src/net/peer/peer.ts | 57 ++++++++++++++++++++++++---- packages/client/src/net/peerpool.ts | 23 +++++------ 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index d6d696ccfa..947b998944 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -1,5 +1,7 @@ +import { BIGINT_0 } from '@ethereumjs/util' import { EventEmitter } from 'events' +import { short } from '../../util' import { BoundEthProtocol, BoundLesProtocol, BoundSnapProtocol } from '../protocol' import type { Config } from '../../config' @@ -92,19 +94,60 @@ export abstract class Peer extends EventEmitter { abstract connect(): Promise /** - * Get latest header of peer + * Eventually updates and returns the latest header of peer */ async latest(): Promise { - const result = await this.eth?.getBlockHeaders({ - block: this.eth!.status.bestHash, + if (!this.eth) { + return + } + let block: bigint | Uint8Array + if (!this.eth!.updatedBestHeader) { + // If there is no updated best header stored yet, start with the status hash + block = this.eth!.status.bestHash + } else { + block = this.getPotentialBestHeaderNum() + } + const result = await this.eth!.getBlockHeaders({ + block, max: 1, }) if (result !== undefined) { - const header = result[1][0] - this.eth!.updatedBestHeader = header - return header + const latest = result[1][0] + this.eth!.updatedBestHeader = latest + if (latest !== undefined) { + const height = latest.number + if ( + this.config.syncTargetHeight === undefined || + this.config.syncTargetHeight === BIGINT_0 || + this.config.syncTargetHeight < latest.number + ) { + this.config.syncTargetHeight = height + this.config.logger.info(`New sync target height=${height} hash=${short(latest.hash())}`) + } + } + } + return this.eth!.updatedBestHeader + } + + /** + * Returns a potential best block header number for the peer + * (not necessarily verified by block request) derived from + * either the client-wide sync target height or the last best + * header timestamp "forward-calculated" by block/slot times (12s). + */ + getPotentialBestHeaderNum(): bigint { + let forwardCalculatedNum = BIGINT_0 + const bestSyncTargetNum = this.config.syncTargetHeight ?? BIGINT_0 + if (this.eth?.updatedBestHeader !== undefined) { + const bestHeaderNum = this.eth!.updatedBestHeader.number + const nowSec = Math.floor(Date.now() / 1000) + const diffSec = nowSec - Number(this.eth!.updatedBestHeader.timestamp) + const SLOT_TIME = 12 + const diffBlocks = BigInt(Math.floor(diffSec / SLOT_TIME)) + forwardCalculatedNum = bestHeaderNum + diffBlocks } - return + const best = forwardCalculatedNum > bestSyncTargetNum ? forwardCalculatedNum : bestSyncTargetNum + return best } /** diff --git a/packages/client/src/net/peerpool.ts b/packages/client/src/net/peerpool.ts index bf052e7db3..6897e63e6d 100644 --- a/packages/client/src/net/peerpool.ts +++ b/packages/client/src/net/peerpool.ts @@ -1,5 +1,4 @@ import { Hardfork } from '@ethereumjs/common' -import { bytesToUnprefixedHex } from '@ethereumjs/util' import { Event } from '../types' @@ -34,9 +33,9 @@ export class PeerPool { private DEFAULT_STATUS_CHECK_INTERVAL = 20000 /** - * Default status check interval (in ms) + * Default peer best header update interval (in ms) */ - private DEFAULT_PEER_ETH_STATUS_CHECK_INTERVAL = 5000 + private DEFAULT_PEER_BEST_HEADER_UPDATE_INTERVAL = 5000 private _statusCheckInterval: NodeJS.Timeout | undefined /* global NodeJS */ private _peerEthStatusCheckInterval: NodeJS.Timeout | undefined @@ -97,8 +96,8 @@ export class PeerPool { this._peerEthStatusCheckInterval = setInterval( // eslint-disable-next-line @typescript-eslint/await-thenable - await this._peerBestBlockUpdate.bind(this), - this.DEFAULT_PEER_ETH_STATUS_CHECK_INTERVAL + await this._peerBestHeaderUpdate.bind(this), + this.DEFAULT_PEER_BEST_HEADER_UPDATE_INTERVAL ) this.running = true @@ -271,18 +270,14 @@ export class PeerPool { } /** - * Periodically check for for the latest best block known by a - * peer to allow for a more accurate best peer choice + * Periodically update the latest best known header for peers */ - async _peerBestBlockUpdate() { + async _peerBestHeaderUpdate() { for (const p of this.peers) { - if (!p.idle && p.eth !== undefined && p instanceof RlpxPeer) { - p.idle = true - console.log(this.config.syncTargetHeight) - //const block = await p.eth.getBlockHeaders() - //console.log(block) - console.log(bytesToUnprefixedHex(p.eth.status.bestHash)) + if (p.idle && p.eth !== undefined && p instanceof RlpxPeer) { p.idle = false + await p.latest() + p.idle = true } } } From 6ea23b08d6a0cd24a28898aefdcd4d4d4918ddb4 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 16:02:21 +0200 Subject: [PATCH 13/40] Client: add Fetcher hack to call peer.latest() --- packages/client/src/sync/fetcher/accountfetcher.ts | 5 +++++ packages/client/src/sync/fetcher/blockfetcher.ts | 6 ++++++ packages/client/src/sync/fetcher/bytecodefetcher.ts | 5 +++++ packages/client/src/sync/fetcher/headerfetcher.ts | 7 +++++++ packages/client/src/sync/fetcher/storagefetcher.ts | 6 ++++++ packages/client/src/sync/fetcher/trienodefetcher.ts | 6 ++++++ 6 files changed, 35 insertions(+) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 79c9c48adc..58725681ee 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -372,6 +372,11 @@ export class AccountFetcher extends Fetcher job: Job ): Promise { const { peer } = job + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() const origin = this.getOrigin(job) const limit = this.getLimit(job) diff --git a/packages/client/src/sync/fetcher/blockfetcher.ts b/packages/client/src/sync/fetcher/blockfetcher.ts index 7aa496604b..79e89b7708 100644 --- a/packages/client/src/sync/fetcher/blockfetcher.ts +++ b/packages/client/src/sync/fetcher/blockfetcher.ts @@ -28,6 +28,12 @@ export class BlockFetcher extends BlockFetcherBase { */ async request(job: Job): Promise { const { task, peer, partialResult } = job + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() + let { first, count } = task if (partialResult) { first = !this.reverse diff --git a/packages/client/src/sync/fetcher/bytecodefetcher.ts b/packages/client/src/sync/fetcher/bytecodefetcher.ts index ea6bb6dd5c..7f76dfc390 100644 --- a/packages/client/src/sync/fetcher/bytecodefetcher.ts +++ b/packages/client/src/sync/fetcher/bytecodefetcher.ts @@ -86,6 +86,11 @@ export class ByteCodeFetcher extends Fetcher job: Job ): Promise { const { task, peer } = job + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() this.debug(`requested code hashes: ${Array.from(task.hashes).map((h) => bytesToHex(h))}`) diff --git a/packages/client/src/sync/fetcher/headerfetcher.ts b/packages/client/src/sync/fetcher/headerfetcher.ts index c2caf9cd5e..9e338f6333 100644 --- a/packages/client/src/sync/fetcher/headerfetcher.ts +++ b/packages/client/src/sync/fetcher/headerfetcher.ts @@ -37,6 +37,13 @@ export class HeaderFetcher extends BlockFetcherBase) { const { task, peer, partialResult } = job + + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() + if (this.flow.maxRequestCount(peer!, 'GetBlockHeaders') < this.config.maxPerRequest) { // we reached our request limit. try with a different peer. return diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index ed173499b4..beb5a60dc4 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -219,6 +219,12 @@ export class StorageFetcher extends Fetcher ): Promise { const { task, peer } = job + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() + const origin = this.getOrigin(job) const limit = this.getLimit(job) diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index 6b0a440264..bbe6c19b29 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -142,6 +142,12 @@ export class TrieNodeFetcher extends Fetcher job: Job ): Promise { const { task, peer } = job + // Currently this is the only safe place to call peer.latest() without interfering with the fetcher + // TODOs: + // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() + // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool + await peer?.latest() + const { paths, pathStrings } = task const rangeResult = await peer!.snap!.getTrieNodes({ From fabf53c995c65a77b5617ecb66b91b8499872778 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Mon, 15 Apr 2024 16:15:56 +0200 Subject: [PATCH 14/40] Various cleanups --- packages/client/src/net/peerpool.ts | 10 ++++------ packages/client/src/net/protocol/rlpxsender.ts | 2 +- packages/devp2p/src/protocol/eth.ts | 4 ++-- packages/devp2p/src/protocol/les.ts | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/client/src/net/peerpool.ts b/packages/client/src/net/peerpool.ts index 6897e63e6d..a504d02e42 100644 --- a/packages/client/src/net/peerpool.ts +++ b/packages/client/src/net/peerpool.ts @@ -38,7 +38,7 @@ export class PeerPool { private DEFAULT_PEER_BEST_HEADER_UPDATE_INTERVAL = 5000 private _statusCheckInterval: NodeJS.Timeout | undefined /* global NodeJS */ - private _peerEthStatusCheckInterval: NodeJS.Timeout | undefined + private _peerBestHeaderUpdateInterval: NodeJS.Timeout | undefined private _reconnectTimeout: NodeJS.Timeout | undefined /** @@ -94,7 +94,7 @@ export class PeerPool { this.DEFAULT_STATUS_CHECK_INTERVAL ) - this._peerEthStatusCheckInterval = setInterval( + this._peerBestHeaderUpdateInterval = setInterval( // eslint-disable-next-line @typescript-eslint/await-thenable await this._peerBestHeaderUpdate.bind(this), this.DEFAULT_PEER_BEST_HEADER_UPDATE_INTERVAL @@ -112,7 +112,7 @@ export class PeerPool { await this.close() } clearInterval(this._statusCheckInterval as NodeJS.Timeout) - clearInterval(this._peerEthStatusCheckInterval as NodeJS.Timeout) + clearInterval(this._peerBestHeaderUpdateInterval as NodeJS.Timeout) clearTimeout(this._reconnectTimeout as NodeJS.Timeout) this.running = false return true @@ -160,9 +160,7 @@ export class PeerPool { * @param filterFn filter function to apply before finding idle peers */ idle(filterFn = (_peer: Peer) => true): Peer | undefined { - const idle = this.peers.filter((p) => { - return p.idle && filterFn(p) ? true : false - }) + const idle = this.peers.filter((p) => p.idle && filterFn(p)) if (idle.length > 0) { const index = Math.floor(Math.random() * idle.length) return idle[index] diff --git a/packages/client/src/net/protocol/rlpxsender.ts b/packages/client/src/net/protocol/rlpxsender.ts index 3c371da70f..f7e248d61c 100644 --- a/packages/client/src/net/protocol/rlpxsender.ts +++ b/packages/client/src/net/protocol/rlpxsender.ts @@ -33,7 +33,7 @@ export class RlpxSender extends Sender { */ sendStatus(status: any) { try { - this.sender.sendStatus(status, true) + this.sender.sendStatus(status) } catch (err: any) { this.emit('error', err) } diff --git a/packages/devp2p/src/protocol/eth.ts b/packages/devp2p/src/protocol/eth.ts index 33eb08a6a4..5772808ad2 100644 --- a/packages/devp2p/src/protocol/eth.ts +++ b/packages/devp2p/src/protocol/eth.ts @@ -271,8 +271,8 @@ export class ETH extends Protocol { return sStr } - sendStatus(status: ETH.StatusOpts, force = false) { - if (this._status !== null && !force) return + sendStatus(status: ETH.StatusOpts) { + if (this._status !== null) return this._status = [ intToBytes(this._version), bigIntToBytes(this._peer.common.chainId()), diff --git a/packages/devp2p/src/protocol/les.ts b/packages/devp2p/src/protocol/les.ts index 18f746acf7..bb38eff6c6 100644 --- a/packages/devp2p/src/protocol/les.ts +++ b/packages/devp2p/src/protocol/les.ts @@ -182,8 +182,8 @@ export class LES extends Protocol { return sStr } - sendStatus(status: LES.Status, force = false) { - if (this._status !== null && !force) return + sendStatus(status: LES.Status) { + if (this._status !== null) return if (status.announceType === undefined) { status['announceType'] = intToBytes(DEFAULT_ANNOUNCE_TYPE) From ccf9366f9c5b420a92dfcab0611c335b03d5d7a2 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 10:55:15 +0200 Subject: [PATCH 15/40] Client: Fix lightsync.spec.ts tests --- packages/client/test/sync/lightsync.spec.ts | 109 +++++++++++++------- 1 file changed, 72 insertions(+), 37 deletions(-) diff --git a/packages/client/test/sync/lightsync.spec.ts b/packages/client/test/sync/lightsync.spec.ts index 2eb77c7c41..6bd6ecec87 100644 --- a/packages/client/test/sync/lightsync.spec.ts +++ b/packages/client/test/sync/lightsync.spec.ts @@ -4,6 +4,7 @@ import { assert, describe, it, vi } from 'vitest' import { Chain } from '../../src/blockchain' import { Config } from '../../src/config' +import { Peer } from '../../src/net/peer/peer' import { HeaderFetcher } from '../../src/sync/fetcher/headerfetcher' import { Event } from '../../src/types' @@ -20,6 +21,9 @@ HeaderFetcher.prototype.clear = td.func() HeaderFetcher.prototype.destroy = td.func() vi.mock('../../src/sync/fetcher/headerfetcher', () => td.object()) +Peer.prototype.latest = td.func() +vi.mock('../../src/net/peer/peer', () => td.object()) + const { LightSynchronizer } = await import('../../src/sync/lightsync') describe('[LightSynchronizer]', async () => { it('should initialize correctly', async () => { @@ -72,15 +76,24 @@ describe('[LightSynchronizer]', async () => { chain, }) sync.best = td.func() - sync.latest = td.func() - td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) - td.when(sync.latest(td.matchers.anything())).thenResolve({ + //sync.latest = td.func() + td.when(sync.best()).thenResolve({ + les: { status: { headNum: BigInt(2) } }, + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) + /*td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), hash: () => new Uint8Array(0), - }) + })*/ + td.when(HeaderFetcher.prototype.fetch(), { delay: 20, times: 2 }).thenResolve(true) ;(sync as any).chain = { headers: { height: BigInt(3) } } - assert.notOk(await sync.sync(), 'local height > remote height') + //assert.notOk(await sync.sync(), 'local height > remote height') ;(sync as any).chain = { headers: { height: BigInt(0) } } setTimeout(() => { config.events.emit(Event.SYNC_SYNCHRONIZED, BigInt(0)) @@ -94,42 +107,51 @@ describe('[LightSynchronizer]', async () => { await sync.stop() await sync.close() vi.unmock('../../src/sync/fetcher/headerfetcher') + vi.unmock('../../src/net/peer/peer') } }) }) describe('sync errors', async () => { - td.reset() - const config = new Config({ accountCache: 10000, storageCache: 1000 }) - const pool = new PeerPool() as any - const chain = await Chain.create({ config }) - const sync = new LightSynchronizer({ - config, - interval: 1, - pool, - chain, - }) - sync.best = td.func() - sync.latest = td.func() - td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) - td.when(sync.latest(td.matchers.anything())).thenResolve({ + it('should produce correct errors', async () => { + td.reset() + const config = new Config({ accountCache: 10000, storageCache: 1000 }) + const pool = new PeerPool() as any + const chain = await Chain.create({ config }) + const sync = new LightSynchronizer({ + config, + interval: 1, + pool, + chain, + }) + sync.best = td.func() + //sync.latest = td.func() + td.when(sync.best()).thenResolve({ + les: { status: { headNum: BigInt(2) } }, + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) + /*td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), hash: () => new Uint8Array(0), - }) - td.when(HeaderFetcher.prototype.fetch()).thenResolve(true) - td.when(HeaderFetcher.prototype.fetch()).thenDo(() => - config.events.emit(Event.SYNC_FETCHED_HEADERS, [] as BlockHeader[]) - ) - config.logger.on('data', async (data) => { - if ((data.message as string).includes('No headers fetched are applicable for import')) { - it('should generate correct warning', () => { + })*/ + td.when(HeaderFetcher.prototype.fetch()).thenResolve(true) + td.when(HeaderFetcher.prototype.fetch()).thenDo(() => + config.events.emit(Event.SYNC_FETCHED_HEADERS, [] as BlockHeader[]) + ) + config.logger.on('data', async (data) => { + if ((data.message as string).includes('No headers fetched are applicable for import')) { assert.ok(true, 'generated correct warning message when no headers received') - }) - config.logger.removeAllListeners() - await sync.stop() - await sync.close() - } + config.logger.removeAllListeners() + await sync.stop() + await sync.close() + } + }) + await sync.sync() }) - await sync.sync() }) describe('import headers', () => { it('should import header', async () => { @@ -138,6 +160,10 @@ describe('import headers', () => { HeaderFetcher.prototype.clear = td.func() HeaderFetcher.prototype.destroy = td.func() vi.mock('../../src/sync/fetcher/headerfetcher', () => td.object()) + + Peer.prototype.latest = td.func() + vi.mock('../../src/net/peer/peer', () => td.object()) + const { LightSynchronizer } = await import('../../src/sync/lightsync') const config = new Config({ accountCache: 10000, @@ -153,12 +179,20 @@ describe('import headers', () => { chain, }) sync.best = td.func() - sync.latest = td.func() - td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) - td.when(sync.latest(td.matchers.anything())).thenResolve({ + //sync.latest = td.func() + td.when(sync.best()).thenResolve({ + les: { status: { headNum: BigInt(2) } }, + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) + /*td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), hash: () => new Uint8Array(0), - }) + })*/ td.when(HeaderFetcher.prototype.fetch()).thenResolve(true) td.when(HeaderFetcher.prototype.fetch()).thenDo(() => config.events.emit(Event.SYNC_FETCHED_HEADERS, [BlockHeader.fromHeaderData({})]) @@ -169,6 +203,7 @@ describe('import headers', () => { config.logger.removeAllListeners() await sync.stop() await sync.close() + vi.unmock('../../src/net/peer/peer') vi.unmock('../../src/sync/fetcher/headerfetcher') } }) From 4cd01aae4f00ea5b339fe7770d9a642552c858ae Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 10:58:33 +0200 Subject: [PATCH 16/40] Some clean-ups --- packages/client/test/sync/lightsync.spec.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/client/test/sync/lightsync.spec.ts b/packages/client/test/sync/lightsync.spec.ts index 6bd6ecec87..a4798be5af 100644 --- a/packages/client/test/sync/lightsync.spec.ts +++ b/packages/client/test/sync/lightsync.spec.ts @@ -76,7 +76,6 @@ describe('[LightSynchronizer]', async () => { chain, }) sync.best = td.func() - //sync.latest = td.func() td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } }, latest: () => { @@ -86,14 +85,10 @@ describe('[LightSynchronizer]', async () => { } }, } as any) - /*td.when(sync.latest(td.matchers.anything())).thenResolve({ - number: BigInt(2), - hash: () => new Uint8Array(0), - })*/ td.when(HeaderFetcher.prototype.fetch(), { delay: 20, times: 2 }).thenResolve(true) ;(sync as any).chain = { headers: { height: BigInt(3) } } - //assert.notOk(await sync.sync(), 'local height > remote height') + assert.notOk(await sync.sync(), 'local height > remote height') ;(sync as any).chain = { headers: { height: BigInt(0) } } setTimeout(() => { config.events.emit(Event.SYNC_SYNCHRONIZED, BigInt(0)) @@ -124,7 +119,6 @@ describe('sync errors', async () => { chain, }) sync.best = td.func() - //sync.latest = td.func() td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } }, latest: () => { @@ -134,10 +128,6 @@ describe('sync errors', async () => { } }, } as any) - /*td.when(sync.latest(td.matchers.anything())).thenResolve({ - number: BigInt(2), - hash: () => new Uint8Array(0), - })*/ td.when(HeaderFetcher.prototype.fetch()).thenResolve(true) td.when(HeaderFetcher.prototype.fetch()).thenDo(() => config.events.emit(Event.SYNC_FETCHED_HEADERS, [] as BlockHeader[]) @@ -179,7 +169,6 @@ describe('import headers', () => { chain, }) sync.best = td.func() - //sync.latest = td.func() td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } }, latest: () => { @@ -189,10 +178,6 @@ describe('import headers', () => { } }, } as any) - /*td.when(sync.latest(td.matchers.anything())).thenResolve({ - number: BigInt(2), - hash: () => new Uint8Array(0), - })*/ td.when(HeaderFetcher.prototype.fetch()).thenResolve(true) td.when(HeaderFetcher.prototype.fetch()).thenDo(() => config.events.emit(Event.SYNC_FETCHED_HEADERS, [BlockHeader.fromHeaderData({})]) From 5777c42ce52acfd058ed42cb7be5855ae5d70bdf Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 11:11:58 +0200 Subject: [PATCH 17/40] Client: Fix beaconsync.spec.ts tests --- packages/client/test/sync/beaconsync.spec.ts | 60 ++++++++++++++------ 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/packages/client/test/sync/beaconsync.spec.ts b/packages/client/test/sync/beaconsync.spec.ts index 5467035ff4..419a923066 100644 --- a/packages/client/test/sync/beaconsync.spec.ts +++ b/packages/client/test/sync/beaconsync.spec.ts @@ -61,10 +61,18 @@ describe('[BeaconSynchronizer]', async () => { const chain = await Chain.create({ config }) const skeleton = new Skeleton({ chain, config, metaDB: new MemoryLevel() }) const sync = new BeaconSynchronizer({ config, pool, chain, execution, skeleton }) - const peer = { eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash' } } } + const peer = { + eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash' } }, + latest: async () => { + return { + number: BigInt(5), + hash: () => new Uint8Array(0), + } + }, + } const headers = [{ number: BigInt(5) }] td.when(peer.eth.getBlockHeaders({ block: 'hash', max: 1 })).thenResolve([BigInt(1), headers]) - const latest = await sync.latest(peer as any) + const latest = await peer.latest() assert.ok(latest!.number === BigInt(5), 'got height') await sync.stop() await sync.close() @@ -78,8 +86,24 @@ describe('[BeaconSynchronizer]', async () => { const sync = new BeaconSynchronizer({ config, pool, chain, execution, skeleton }) ;(sync as any).running = true const peers = [ - { eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash1' }, inbound: false } }, - { eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash2' }, inbound: false } }, + { + eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash1' }, inbound: false }, + latest: () => { + return { + number: BigInt(5), + hash: () => new Uint8Array(0), + } + }, + }, + { + eth: { getBlockHeaders: td.func(), status: { bestHash: 'hash2' }, inbound: false }, + latest: () => { + return { + number: BigInt(10), + hash: () => new Uint8Array(0), + } + }, + }, ] td.when(peers[0].eth.getBlockHeaders({ block: 'hash1', max: 1 })).thenResolve([ BigInt(1), @@ -111,12 +135,14 @@ describe('[BeaconSynchronizer]', async () => { const sync = new BeaconSynchronizer({ config, pool, chain, execution, skeleton }) sync.best = td.func() - sync.latest = td.func() - td.when(sync.best()).thenResolve('peer') - td.when(sync.latest('peer' as any)).thenResolve({ - number: BigInt(2), - hash: () => new Uint8Array(0), - }) + td.when(sync.best()).thenResolve({ + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) td.when(ReverseBlockFetcher.prototype.fetch(), { delay: 100, times: 3 }).thenResolve(false) ;(skeleton as any).status.progress.subchains = [ { head: BigInt(10), tail: BigInt(6) }, @@ -162,12 +188,14 @@ describe('[BeaconSynchronizer]', async () => { await skeleton.open() const sync = new BeaconSynchronizer({ config, pool, chain, execution, skeleton }) sync.best = td.func() - sync.latest = td.func() - td.when(sync.best()).thenResolve('peer') - td.when(sync.latest('peer' as any)).thenResolve({ - number: BigInt(2), - hash: () => new Uint8Array(0), - }) + td.when(sync.best()).thenResolve({ + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) td.when(ReverseBlockFetcher.prototype.fetch(), { delay: 100, times: 1 }).thenResolve(false) ;(skeleton as any).status.progress.subchains = [{ head: BigInt(10), tail: BigInt(6) }] ;(sync as any).chain = { From 9ea28d8f2f8b65dc28579b312d535179fe4fb056 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 11:19:00 +0200 Subject: [PATCH 18/40] Client: Fix fullsync.spec.tst tests --- packages/client/test/sync/fullsync.spec.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/client/test/sync/fullsync.spec.ts b/packages/client/test/sync/fullsync.spec.ts index 3639b024bf..7214fce000 100644 --- a/packages/client/test/sync/fullsync.spec.ts +++ b/packages/client/test/sync/fullsync.spec.ts @@ -1,4 +1,5 @@ import { Block } from '@ethereumjs/block' +import * as td from 'testdouble' import { assert, describe, it, vi } from 'vitest' import { Chain } from '../../src/blockchain' @@ -74,8 +75,14 @@ describe('[FullSynchronizer]', async () => { }), status: { bestHash: 'hash' }, }, + latest: async () => { + return { + number: BigInt(5), + hash: () => new Uint8Array(0), + } + }, } - const latest = await sync.latest(peer as any) + const latest = await peer.latest() assert.equal(latest!.number, BigInt(5), 'got height') await sync.stop() await sync.close() @@ -128,14 +135,16 @@ describe('[FullSynchronizer]', async () => { txPool, execution, }) - sync.best = vi.fn().mockResolvedValue('peer') - sync.latest = vi.fn((input) => { - if (input === ('peer' as any)) + sync.best = td.func() + td.when(sync.best()).thenResolve({ + les: { status: { headNum: BigInt(2) } }, + latest: () => { return { number: BigInt(2), hash: () => new Uint8Array(0), } - }) as any + }, + } as any) let count = 0 BlockFetcher.prototype.fetch = vi.fn(async () => { if (count < 2) { From 9a6ae272836094474f9ed2a8bcf07293573d47d6 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 11:21:39 +0200 Subject: [PATCH 19/40] Client: Fix snapsync.spec.ts tests --- packages/client/test/sync/snapsync.spec.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/client/test/sync/snapsync.spec.ts b/packages/client/test/sync/snapsync.spec.ts index 2b5077ff1f..62f6f7a422 100644 --- a/packages/client/test/sync/snapsync.spec.ts +++ b/packages/client/test/sync/snapsync.spec.ts @@ -83,11 +83,23 @@ describe('[SnapSynchronizer]', async () => { snap: {}, eth: { status: { bestHash: '0xaa' }, getBlockHeaders: getBlockHeaders1 }, inbound: false, + latest: () => { + return { + number: BigInt(1), + hash: () => new Uint8Array(0), + } + }, }, { snap: {}, eth: { status: { bestHash: '0xbb' }, getBlockHeaders: getBlockHeaders2 }, inbound: false, + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, }, ] ;(sync as any).pool = { peers } From 5c42d73031e802f59cc9cb6163c3187ea5a94cc3 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 11:41:22 +0200 Subject: [PATCH 20/40] Client: Fix fetcher tests --- packages/client/test/sync/fetcher/accountfetcher.spec.ts | 5 +++++ packages/client/test/sync/fetcher/blockfetcher.spec.ts | 2 ++ packages/client/test/sync/fetcher/bytecodefetcher.spec.ts | 1 + packages/client/test/sync/fetcher/fetcher.spec.ts | 2 +- packages/client/test/sync/fetcher/headerfetcher.spec.ts | 1 + .../client/test/sync/fetcher/reverseblockfetcher.spec.ts | 1 + packages/client/test/sync/fetcher/storagefetcher.spec.ts | 4 ++++ packages/client/test/sync/fetcher/trienodefetcher.spec.ts | 2 ++ 8 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/client/test/sync/fetcher/accountfetcher.spec.ts b/packages/client/test/sync/fetcher/accountfetcher.spec.ts index 507094524e..c697ec49c4 100644 --- a/packages/client/test/sync/fetcher/accountfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/accountfetcher.spec.ts @@ -184,6 +184,7 @@ describe('[AccountFetcher]', async () => { snap: { getAccountRange: vi.fn() }, id: 'random', address: 'random', + latest: vi.fn(), } const partialResult: any = [ [ @@ -243,6 +244,7 @@ describe('[AccountFetcher]', async () => { }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) @@ -269,6 +271,7 @@ describe('[AccountFetcher]', async () => { snap: { getAccountRange: mockedGetAccountRange }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } @@ -307,6 +310,7 @@ describe('[AccountFetcher]', async () => { snap: { getAccountRange: mockedGetAccountRange }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } @@ -352,6 +356,7 @@ describe('[AccountFetcher]', async () => { snap: { getAccountRange: mockedGetAccountRange }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } const results = await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/blockfetcher.spec.ts b/packages/client/test/sync/fetcher/blockfetcher.spec.ts index 7bb2bd73c1..27d125ed61 100644 --- a/packages/client/test/sync/fetcher/blockfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/blockfetcher.spec.ts @@ -179,6 +179,7 @@ describe('[BlockFetcher]', async () => { }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) @@ -227,6 +228,7 @@ describe('[BlockFetcher]', async () => { }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } const resp = await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/bytecodefetcher.spec.ts b/packages/client/test/sync/fetcher/bytecodefetcher.spec.ts index 654a776664..d4a6a4dc9f 100644 --- a/packages/client/test/sync/fetcher/bytecodefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/bytecodefetcher.spec.ts @@ -140,6 +140,7 @@ describe('[ByteCodeFetcher]', async () => { snap: { getByteCodes: mockedGetByteCodes }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } const results = await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/fetcher.spec.ts b/packages/client/test/sync/fetcher/fetcher.spec.ts index 4081f46fbf..a0d7b875fb 100644 --- a/packages/client/test/sync/fetcher/fetcher.spec.ts +++ b/packages/client/test/sync/fetcher/fetcher.spec.ts @@ -66,7 +66,7 @@ describe('should handle expiration', async () => { timeout: 5, }) const job = { index: 0 } - const peer = { idle: true } + const peer = { idle: true, latest: vi.fn() } fetcher.peer = vi.fn().mockReturnValue(() => peer) fetcher.request = vi.fn().mockImplementationOnce(async (job, peer) => { await new Promise((resolve) => { diff --git a/packages/client/test/sync/fetcher/headerfetcher.spec.ts b/packages/client/test/sync/fetcher/headerfetcher.spec.ts index 9e08f4782d..9afb5d7825 100644 --- a/packages/client/test/sync/fetcher/headerfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/headerfetcher.spec.ts @@ -84,6 +84,7 @@ describe('[HeaderFetcher]', async () => { les: { getBlockHeaders: vi.fn() }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts b/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts index e85f6fb73c..a911342f1c 100644 --- a/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/reverseblockfetcher.spec.ts @@ -169,6 +169,7 @@ describe('[ReverseBlockFetcher]', async () => { }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/storagefetcher.spec.ts b/packages/client/test/sync/fetcher/storagefetcher.spec.ts index 30d368e3d4..64eb1ad79f 100644 --- a/packages/client/test/sync/fetcher/storagefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/storagefetcher.spec.ts @@ -299,6 +299,7 @@ describe('[StorageFetcher]', async () => { snap: { getStorageRanges: mockedGetStorageRanges }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) @@ -347,6 +348,7 @@ describe('[StorageFetcher]', async () => { snap: { getStorageRanges: mockedGetStorageRanges }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } @@ -390,6 +392,7 @@ describe('[StorageFetcher]', async () => { snap: { getStorageRanges: mockedGetStorageRanges }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, task } @@ -446,6 +449,7 @@ describe('[StorageFetcher]', async () => { snap: { getStorageRanges: mockedGetStorageRanges }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } let results = await fetcher.request(job as any) diff --git a/packages/client/test/sync/fetcher/trienodefetcher.spec.ts b/packages/client/test/sync/fetcher/trienodefetcher.spec.ts index d4b1202801..1551716746 100644 --- a/packages/client/test/sync/fetcher/trienodefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/trienodefetcher.spec.ts @@ -109,6 +109,7 @@ describe('[TrieNodeFetcher]', async () => { }, id: 'random', address: 'random', + latest: vi.fn(), } const job = { peer, partialResult, task } await fetcher.request(job as any) @@ -148,6 +149,7 @@ describe('[TrieNodeFetcher]', async () => { snap: { getTrieNodes: mockedGetTrieNodes }, id: 'random', address: 'random', + latest: vi.fn(), } const task = { pathStrings: [''], From 9e60a092146b2be05faf4e5533c9a45f8d6fe214 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 11:49:48 +0200 Subject: [PATCH 21/40] Client: Fix eth syncing.spec.ts tests --- packages/client/test/rpc/eth/syncing.spec.ts | 25 +++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/client/test/rpc/eth/syncing.spec.ts b/packages/client/test/rpc/eth/syncing.spec.ts index c773b06094..de0d06c0fc 100644 --- a/packages/client/test/rpc/eth/syncing.spec.ts +++ b/packages/client/test/rpc/eth/syncing.spec.ts @@ -42,9 +42,13 @@ describe(method, () => { const manager = createManager(client) const rpcServer = startRPC(manager.getMethods()) const rpc = getRpcClient(rpcServer) - const synchronizer = client.services[0].synchronizer! - synchronizer.best = td.func() - td.when(synchronizer.best()).thenResolve('peer') + const sync = client.services[0].synchronizer! + sync.best = td.func() + td.when(sync.best()).thenResolve({ + latest: () => { + return + }, + } as any) client.config.synchronized = false assert.equal(client.config.synchronized, false, 'not synchronized yet') @@ -60,11 +64,16 @@ describe(method, () => { const manager = createManager(client) const rpcServer = startRPC(manager.getMethods()) const rpc = getRpcClient(rpcServer) - const synchronizer = client.services[0].synchronizer as FullSynchronizer - synchronizer.best = td.func() - synchronizer.latest = td.func() - td.when(synchronizer.best()).thenResolve('peer') - td.when(synchronizer.latest('peer' as any)).thenResolve({ number: BigInt(2) }) + const sync = client.services[0].synchronizer as FullSynchronizer + sync.best = td.func() + td.when(sync.best()).thenResolve({ + latest: () => { + return { + number: BigInt(2), + hash: () => new Uint8Array(0), + } + }, + } as any) client.config.synchronized = false assert.equal(client.config.synchronized, false, 'not synchronized yet') From d6c2a6c62ea0b2966dfd3593d38a5bd393a55194 Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 13:20:38 +0200 Subject: [PATCH 22/40] Client: Fix integration tests --- packages/client/src/net/peer/peer.ts | 7 ++++--- packages/client/test/integration/mocks/mockpeer.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/client/src/net/peer/peer.ts b/packages/client/src/net/peer/peer.ts index 947b998944..a814ed9af3 100644 --- a/packages/client/src/net/peer/peer.ts +++ b/packages/client/src/net/peer/peer.ts @@ -117,9 +117,10 @@ export abstract class Peer extends EventEmitter { if (latest !== undefined) { const height = latest.number if ( - this.config.syncTargetHeight === undefined || - this.config.syncTargetHeight === BIGINT_0 || - this.config.syncTargetHeight < latest.number + height > BIGINT_0 && + (this.config.syncTargetHeight === undefined || + this.config.syncTargetHeight === BIGINT_0 || + this.config.syncTargetHeight < latest.number) ) { this.config.syncTargetHeight = height this.config.logger.info(`New sync target height=${height} hash=${short(latest.hash())}`) diff --git a/packages/client/test/integration/mocks/mockpeer.ts b/packages/client/test/integration/mocks/mockpeer.ts index 182fb47dd1..cf935afde4 100644 --- a/packages/client/test/integration/mocks/mockpeer.ts +++ b/packages/client/test/integration/mocks/mockpeer.ts @@ -11,6 +11,7 @@ import { createStream } from './network' import type { PeerOptions } from '../../../src/net/peer' import type { MockServer } from './mockserver' import type { RemoteStream } from './network' +import type { BlockHeader } from '@ethereumjs/block' // TypeScript doesn't have support yet for ReturnType // with generic types, so this wrapper is used as a helper. @@ -40,6 +41,13 @@ export class MockPeer extends Peer { this.config.events.emit(Event.PEER_CONNECTED, this) } + async latest(): Promise { + if (this.eth !== undefined) { + this.eth.updatedBestHeader = undefined + } + return super.latest() + } + async accept(server: MockServer) { if (this.connected) { return From 58cb2b4eaff75d8c2c6b6e5c934bc5c6456b75ea Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 13:39:53 +0200 Subject: [PATCH 23/40] Client: Backup lightsync integration tests (lightsync not supported anymore) --- .../integration/{lightsync.spec.ts => lightsync.spec.backup.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/client/test/integration/{lightsync.spec.ts => lightsync.spec.backup.ts} (100%) diff --git a/packages/client/test/integration/lightsync.spec.ts b/packages/client/test/integration/lightsync.spec.backup.ts similarity index 100% rename from packages/client/test/integration/lightsync.spec.ts rename to packages/client/test/integration/lightsync.spec.backup.ts From c53fa4ba605ac152188a9ad1a731db0af9bfda3e Mon Sep 17 00:00:00 2001 From: Holger Drewes Date: Tue, 16 Apr 2024 13:41:18 +0200 Subject: [PATCH 24/40] Lightsync tests deprecation note --- .../client/test/integration/lightsync.spec.ts | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 packages/client/test/integration/lightsync.spec.ts diff --git a/packages/client/test/integration/lightsync.spec.ts b/packages/client/test/integration/lightsync.spec.ts new file mode 100644 index 0000000000..2630630f93 --- /dev/null +++ b/packages/client/test/integration/lightsync.spec.ts @@ -0,0 +1,100 @@ +import { assert, describe, it } from 'vitest' + +import { SyncMode } from '../../src/config' +import { Event } from '../../src/types' + +import { destroy, setup, wait } from './util' + +/** + * Lightsync is not actively supported anymore. + * + * Tests are kept for now until a more finalized decision is taken + * on the topic. // 2024-04-16 + */ +describe( + 'should sync headers', + async () => { + const [remoteServer, remoteService] = await setup({ + location: '127.0.0.2', + height: 20, + syncmode: SyncMode.Full, + }) + const [localServer, localService] = await setup({ + location: '127.0.0.1', + height: 0, + syncmode: SyncMode.Light, + }) + await localService.synchronizer!.stop() + await localServer.discover('remotePeer1', '127.0.0.2') + localService.config.events.on(Event.SYNC_SYNCHRONIZED, async () => { + it('should sync', () => { + assert.equal(localService.chain.headers.height, BigInt(20), 'synced') + }) + await destroy(localServer, localService) + await destroy(remoteServer, remoteService) + }) + await localService.synchronizer!.start() + }, + { timeout: 30000 } +) + +describe( + 'should not sync with stale peers', + async () => { + const [remoteServer, remoteService] = await setup({ + location: '127.0.0.2', + height: 9, + syncmode: SyncMode.Full, + }) + const [localServer, localService] = await setup({ + location: '127.0.0.1', + height: 10, + syncmode: SyncMode.Light, + }) + localService.config.events.on(Event.SYNC_SYNCHRONIZED, async () => { + throw new Error('synced with a stale peer') + }) + await localServer.discover('remotePeer', '127.0.0.2') + await wait(100) + await destroy(localServer, localService) + await destroy(remoteServer, remoteService) + it('should not sync', async () => { + assert.ok('did not sync') + }) + }, + { timeout: 30000 } +) + +describe( + 'should sync with best peer', + async () => { + const [remoteServer1, remoteService1] = await setup({ + location: '127.0.0.2', + height: 9, + syncmode: SyncMode.Full, + }) + const [remoteServer2, remoteService2] = await setup({ + location: '127.0.0.3', + height: 10, + syncmode: SyncMode.Full, + }) + const [localServer, localService] = await setup({ + location: '127.0.0.1', + height: 0, + syncmode: SyncMode.Light, + }) + await localService.synchronizer!.stop() + await localServer.discover('remotePeer1', '127.0.0.2') + await localServer.discover('remotePeer2', '127.0.0.3') + localService.config.events.on(Event.SYNC_SYNCHRONIZED, async () => { + it('should sync with best peer', async () => { + assert.equal(localService.chain.headers.height, BigInt(10), 'synced with best peer') + }) + await destroy(localServer, localService) + await destroy(remoteServer1, remoteService1) + await destroy(remoteServer2, remoteService2) + }) + await localService.synchronizer!.start() + }, + { timeout: 30000 } +) From fe9a4ecd324e3945b8c68320a9dbf6b9d44d9b74 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 17 Apr 2024 16:28:05 -0400 Subject: [PATCH 25/40] Make lightsync tests run (but fail) --- .../client/test/integration/lightsync.spec.ts | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/packages/client/test/integration/lightsync.spec.ts b/packages/client/test/integration/lightsync.spec.ts index 2630630f93..6508181544 100644 --- a/packages/client/test/integration/lightsync.spec.ts +++ b/packages/client/test/integration/lightsync.spec.ts @@ -11,9 +11,8 @@ import { destroy, setup, wait } from './util' * Tests are kept for now until a more finalized decision is taken * on the topic. // 2024-04-16 */ -describe( - 'should sync headers', - async () => { +describe('light sync integration tests', () => { + it('should sync headers', async () => { const [remoteServer, remoteService] = await setup({ location: '127.0.0.2', height: 20, @@ -34,13 +33,9 @@ describe( await destroy(remoteServer, remoteService) }) await localService.synchronizer!.start() - }, - { timeout: 30000 } -) + }, 30000) -describe( - 'should not sync with stale peers', - async () => { + it('should not sync with stale peers', async () => { const [remoteServer, remoteService] = await setup({ location: '127.0.0.2', height: 9, @@ -61,13 +56,9 @@ describe( it('should not sync', async () => { assert.ok('did not sync') }) - }, - { timeout: 30000 } -) + }, 30000) -describe( - 'should sync with best peer', - async () => { + it('should sync with best peer', async () => { const [remoteServer1, remoteService1] = await setup({ location: '127.0.0.2', height: 9, @@ -95,6 +86,5 @@ describe( await destroy(remoteServer2, remoteService2) }) await localService.synchronizer!.start() - }, - { timeout: 30000 } -) + }, 30000) +}) From d8028eb0fe65a28b27f6e6f28058b782834cf920 Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Wed, 17 Apr 2024 18:24:01 -0700 Subject: [PATCH 26/40] Receive peer updates only in request function of fetchers --- .../client/src/service/fullethereumservice.ts | 7 +---- packages/client/src/sync/snapsync.ts | 30 ++++++++----------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/client/src/service/fullethereumservice.ts b/packages/client/src/service/fullethereumservice.ts index eeeea114aa..c1521bdc73 100644 --- a/packages/client/src/service/fullethereumservice.ts +++ b/packages/client/src/service/fullethereumservice.ts @@ -243,12 +243,7 @@ export class FullEthereumService extends Service { * vm execution */ async buildHeadState(): Promise { - // If during building FCU identifies a new root/height to sync to, evalueate new height with sync - // strategy and initiate state healing and fetcher root updates for snap sync - if (this.building) { - const peer = await this.snapsync?.best() - void this.snapsync?.syncWithPeer(peer) - } + if (this.building) return this.building = true try { diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index b93a9a4395..bd3657eade 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -206,7 +206,10 @@ export class SnapSynchronizer extends Synchronizer { this.config.logger.info(`New sync target height=${height} hash=${bytesToHex(latest.hash())}`) } - if (this.config.syncTargetHeight <= latest.number + this.config.snapAvailabilityDepth) { + if ( + (this.fetcher === null || this.fetcher.syncErrored !== undefined) && + this.config.syncTargetHeight <= latest.number + this.config.snapAvailabilityDepth + ) { if ((this.fetcherDoneFlags.snapTargetHeight ?? BIGINT_0) < latest.number) { this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot @@ -222,22 +225,15 @@ export class SnapSynchronizer extends Synchronizer { : 'previous fetcher errored=' + this.fetcher.syncErrored?.message }` ) - - if (this.fetcher === null || this.fetcher.syncErrored !== undefined) { - this.fetcher = new AccountFetcher({ - config: this.config, - pool: this.pool, - stateManager: this.execution.vm.stateManager as DefaultStateManager, - root: stateRoot, - // This needs to be determined from the current state of the MPT dump - first: BigInt(0), - fetcherDoneFlags: this.fetcherDoneFlags, - }) - } else { - // update root - this.config.logger.info(`UPDATING ROOTS`) - this.fetcher?.updateStateRoot(stateRoot) - } + this.fetcher = new AccountFetcher({ + config: this.config, + pool: this.pool, + stateManager: this.execution.vm.stateManager as DefaultStateManager, + root: stateRoot, + // This needs to be determined from the current state of the MPT dump + first: BigInt(0), + fetcherDoneFlags: this.fetcherDoneFlags, + }) } else { return false } From 52d1f6e21188c44b2770baee0c870cbfe9b129bc Mon Sep 17 00:00:00 2001 From: Indigo Alpha Date: Wed, 17 Apr 2024 21:14:41 -0700 Subject: [PATCH 27/40] Update fetcher roots on new latestHeader --- .../client/src/sync/fetcher/accountfetcher.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 614115b13f..da724fa2fe 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -1,3 +1,4 @@ +import type { BlockHeader } from '@ethereumjs/block' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { @@ -100,6 +101,7 @@ export class AccountFetcher extends Fetcher this.accountTrie = this.stateManager['_getAccountTrie']() this.debug = createDebugLogger('client:AccountFetcher') + this.debug('dbg101') this.storageFetcher = new StorageFetcher({ config: this.config, @@ -376,7 +378,22 @@ export class AccountFetcher extends Fetcher // TODOs: // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool - await peer?.latest() + this.debug('dbg100') + const latestHeader = await peer?.latest() + if (latestHeader !== undefined) { + const latestStateRoot = (latestHeader as BlockHeader).stateRoot + this.debug(this.root) + this.debug(bytesToHex(latestStateRoot)) + if (!equalsBytes(this.root, latestStateRoot)) { + this.debug( + `updating fetcher roots from ${bytesToHex(this.root)} over to ${bytesToHex( + latestStateRoot + )}` + ) + this.updateStateRoot(latestStateRoot) + } + } + const origin = this.getOrigin(job) const limit = this.getLimit(job) From d469538460928d70e1d43b77eee974044e93e51c Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 9 Jul 2024 15:07:31 -0700 Subject: [PATCH 28/40] Latest changes for integrating updated roots from latest function --- .../client/src/service/fullethereumservice.ts | 32 ++++++++++++++++++- .../client/src/sync/fetcher/accountfetcher.ts | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/client/src/service/fullethereumservice.ts b/packages/client/src/service/fullethereumservice.ts index 94268bd9d4..a78bc19c51 100644 --- a/packages/client/src/service/fullethereumservice.ts +++ b/packages/client/src/service/fullethereumservice.ts @@ -11,6 +11,7 @@ import { LesProtocol } from '../net/protocol/lesprotocol.js' import { SnapProtocol } from '../net/protocol/snapprotocol.js' import { BeaconSynchronizer, FullSynchronizer, SnapSynchronizer } from '../sync/index.js' import { Event } from '../types.js' +import { wait } from '../util/wait.js' import { Service, type ServiceOptions } from './service.js' import { Skeleton } from './skeleton.js' @@ -243,7 +244,36 @@ export class FullEthereumService extends Service { * vm execution */ async buildHeadState(): Promise { - if (this.building) return + if (this.building) { + if (this.snapsync !== undefined) { + // discover best peer + let peer = await this.snapsync.best() + let numAttempts = 1 + while (!peer && this.snapsync.opened) { + this.snapsync.config.logger.debug(`Waiting for best peer (attempt #${numAttempts})`) + await wait(5000) + peer = await this.snapsync.best() + numAttempts += 1 + } + + if (peer === undefined) throw new Error('Unable to find a peer to sync with') + const latest = await peer.latest() + + if (latest === undefined || latest.stateRoot === undefined) { + // TODO if latest is undefined, latest known root is assumed to not yet have been updated? + return + } + + // TODO only update root if it is a new one compared to what's already being used + const fetcher = this.snapsync.fetcher + if (fetcher === null) { + return + } + fetcher.updateStateRoot(latest!.stateRoot as Uint8Array) + } + + return + } this.building = true try { diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 8fe597189a..400ffbfdfa 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -278,6 +278,8 @@ export class AccountFetcher extends Fetcher } break case TrieNodeFetcher: + // TODO update to check if heal phase completed successfully and then continue with next + // healing phase fetcherDoneFlags.trieNodeFetcher.done = true break } From 87ee2aa237815a1b4a8d9db14aca4606bb5df7f4 Mon Sep 17 00:00:00 2001 From: Amir Date: Tue, 9 Jul 2024 16:54:22 -0700 Subject: [PATCH 29/40] fix linting issue --- packages/client/src/sync/fetcher/accountfetcher.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 400ffbfdfa..9f58163570 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -1,4 +1,3 @@ -import type { BlockHeader } from '@ethereumjs/block' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' import { From 50fb30b09aae11a181449d30af68a42372155eec Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 26 Jul 2024 10:34:47 -0700 Subject: [PATCH 30/40] Move over to getting latest in request function of relevant fetchers --- .../client/src/service/fullethereumservice.ts | 32 +-------- .../client/src/sync/fetcher/accountfetcher.ts | 72 ++++++++++++------- .../client/src/sync/fetcher/storagefetcher.ts | 34 ++++++--- .../src/sync/fetcher/trienodefetcher.ts | 27 ++++--- packages/client/src/sync/snapsync.ts | 1 + 5 files changed, 91 insertions(+), 75 deletions(-) diff --git a/packages/client/src/service/fullethereumservice.ts b/packages/client/src/service/fullethereumservice.ts index a78bc19c51..94268bd9d4 100644 --- a/packages/client/src/service/fullethereumservice.ts +++ b/packages/client/src/service/fullethereumservice.ts @@ -11,7 +11,6 @@ import { LesProtocol } from '../net/protocol/lesprotocol.js' import { SnapProtocol } from '../net/protocol/snapprotocol.js' import { BeaconSynchronizer, FullSynchronizer, SnapSynchronizer } from '../sync/index.js' import { Event } from '../types.js' -import { wait } from '../util/wait.js' import { Service, type ServiceOptions } from './service.js' import { Skeleton } from './skeleton.js' @@ -244,36 +243,7 @@ export class FullEthereumService extends Service { * vm execution */ async buildHeadState(): Promise { - if (this.building) { - if (this.snapsync !== undefined) { - // discover best peer - let peer = await this.snapsync.best() - let numAttempts = 1 - while (!peer && this.snapsync.opened) { - this.snapsync.config.logger.debug(`Waiting for best peer (attempt #${numAttempts})`) - await wait(5000) - peer = await this.snapsync.best() - numAttempts += 1 - } - - if (peer === undefined) throw new Error('Unable to find a peer to sync with') - const latest = await peer.latest() - - if (latest === undefined || latest.stateRoot === undefined) { - // TODO if latest is undefined, latest known root is assumed to not yet have been updated? - return - } - - // TODO only update root if it is a new one compared to what's already being used - const fetcher = this.snapsync.fetcher - if (fetcher === null) { - return - } - fetcher.updateStateRoot(latest!.stateRoot as Uint8Array) - } - - return - } + if (this.building) return this.building = true try { diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 9f58163570..519ab58c85 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -46,6 +46,8 @@ export interface AccountFetcherOptions extends FetcherOptions { /** Root hash of the account trie to serve */ root: Uint8Array + height: bigint + /** The origin to start account fetcher from (including), by default starts from 0 (0x0000...) */ first: bigint /** The range to eventually, by default should be set at BIGINT_2 ** BigInt(256) + BIGINT_1 - first */ @@ -73,6 +75,7 @@ export class AccountFetcher extends Fetcher accountTrie: Trie root: Uint8Array + height: bigint highestKnownHash: Uint8Array | undefined /** The origin to start account fetcher from (including), by default starts from 0 (0x0000...) */ @@ -93,6 +96,7 @@ export class AccountFetcher extends Fetcher this.fetcherDoneFlags = options.fetcherDoneFlags ?? getInitFecherDoneFlags() this.root = options.root + this.height = options.height this.first = options.first this.count = options.count ?? BIGINT_2EXP256 - this.first @@ -100,12 +104,12 @@ export class AccountFetcher extends Fetcher this.accountTrie = this.stateManager['_getAccountTrie']() this.debug = createDebugLogger('client:AccountFetcher') - this.debug('dbg101') this.storageFetcher = new StorageFetcher({ config: this.config, pool: this.pool, - root: this.root, + root: this.fetcherDoneFlags.snapTargetRoot!, + height: this.height, storageRequests: [], first: BIGINT_1, destroyWhenDone: false, @@ -123,7 +127,8 @@ export class AccountFetcher extends Fetcher this.trieNodeFetcher = new TrieNodeFetcher({ config: this.config, pool: this.pool, - root: this.root, + root: this.fetcherDoneFlags.snapTargetRoot!, + height: this.height, stateManager: this.stateManager, destroyWhenDone: false, fetcherDoneFlags: this.fetcherDoneFlags, @@ -138,9 +143,9 @@ export class AccountFetcher extends Fetcher const limit = this.getLimit(syncRange) this.debug( - `Account fetcher instantiated root=${short(this.root)} origin=${short(origin)} limit=${short( - limit - )} destroyWhenDone=${this.destroyWhenDone}` + `Account fetcher instantiated root=${short( + this.fetcherDoneFlags.snapTargetRoot! + )} origin=${short(origin)} limit=${short(limit)} destroyWhenDone=${this.destroyWhenDone}` ) } @@ -288,15 +293,21 @@ export class AccountFetcher extends Fetcher accountFetcher.done && storageFetcher.done && byteCodeFetcher.done && trieNodeFetcher.done this.config.superMsg( - `snapFetchersCompletion root=${short(this.root)} accountsRoot=${short( - fetcherDoneFlags.stateRoot ?? 'na' - )} done=${this.fetcherDoneFlags.done} accountsDone=${accountFetcher.done} storageDone=${ - storageFetcher.done - } byteCodesDone=${byteCodeFetcher.done} trieNodesDone=${trieNodeFetcher.done}` + `snapFetchersCompletion root=${short( + this.fetcherDoneFlags.snapTargetRoot! + )} accountsRoot=${short(fetcherDoneFlags.stateRoot ?? 'na')} done=${ + this.fetcherDoneFlags.done + } accountsDone=${accountFetcher.done} storageDone=${storageFetcher.done} byteCodesDone=${ + byteCodeFetcher.done + } trieNodesDone=${trieNodeFetcher.done}` ) if (this.fetcherDoneFlags.done) { - this.config.events.emit(Event.SYNC_SNAPSYNC_COMPLETE, this.root, this.stateManager) + this.config.events.emit( + Event.SYNC_SNAPSYNC_COMPLETE, + this.fetcherDoneFlags.snapTargetRoot!, + this.stateManager + ) } } @@ -379,10 +390,24 @@ export class AccountFetcher extends Fetcher // TODOs: // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool - await peer?.latest() + const latest = await peer?.latest() + if (latest !== undefined && latest.stateRoot !== undefined) { + // TODO currently doing this check and update both in account and trienode fetchers since they + // could be running independently of eachother in some cases + const currentHeight = this.height + const newestHeight = latest.number + if (newestHeight - currentHeight >= 1) { + this.fetcherDoneFlags.snapTargetHeight = latest.number + this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot + this.fetcherDoneFlags.snapTargetHash = latest.hash() + + // TODO do we need to set this one since it's a duplicate of snapTargetRoot? + this.fetcherDoneFlags.stateRoot = latest.stateRoot + } + } + const origin = this.getOrigin(job) const limit = this.getLimit(job) - if (this.highestKnownHash && compareBytes(limit, this.highestKnownHash) < 0) { // skip this job and don't rerequest it if it's limit is lower than the highest known key hash this.debug(`skipping request with limit lower than highest known hash`) @@ -390,7 +415,7 @@ export class AccountFetcher extends Fetcher } const rangeResult = await peer!.snap!.getAccountRange({ - root: this.root, + root: this.fetcherDoneFlags.snapTargetRoot!, origin, limit, bytes: BigInt(this.config.maxRangeBytes), @@ -407,7 +432,7 @@ export class AccountFetcher extends Fetcher if (rangeResult.proof.length > 0) { try { const isMissingRightRange = await Trie.verifyRangeProof( - this.root, + this.fetcherDoneFlags.snapTargetRoot!, origin, null, [], @@ -434,7 +459,11 @@ export class AccountFetcher extends Fetcher try { // verifyRangeProof will also verify validate there are no missed states between origin and // response data - const isMissingRightRange = await this.verifyRangeProof(this.root, origin, rangeResult) + const isMissingRightRange = await this.verifyRangeProof( + this.fetcherDoneFlags.snapTargetRoot!, + origin, + rangeResult + ) // Check if there is any pending data to be synced to the right let completed: boolean @@ -586,15 +615,6 @@ export class AccountFetcher extends Fetcher return tasks } - updateStateRoot(stateRoot: Uint8Array) { - this.root = stateRoot - - this.storageFetcher.updateStateRoot(stateRoot) - - // TODO trieNodeFetcher needs to be constantly healing while other fetchers work - this.trieNodeFetcher.updateStateRoot(stateRoot) - } - nextTasks(): void { if ( this.in.length === 0 && diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index d59c848fdb..bc3be98f48 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -45,6 +45,8 @@ export interface StorageFetcherOptions extends FetcherOptions { /** Root hash of the account trie to serve */ root: Uint8Array + height: bigint + /** Storage requests to fetch */ storageRequests?: StorageRequest[] @@ -70,6 +72,7 @@ export type JobTask = { export class StorageFetcher extends Fetcher { protected debug: Debugger root: Uint8Array + height: bigint stateManager: DefaultStateManager fetcherDoneFlags: SnapFetcherDoneFlags @@ -89,6 +92,7 @@ export class StorageFetcher extends Fetcher allow to at least place in Fetcher.next() // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool - await peer?.latest() + const latest = await peer?.latest() + if (latest !== undefined && latest.stateRoot !== undefined) { + // TODO currently doing this check and update both in account and trienode fetchers since they + // could be running independently of eachother in some cases + const currentHeight = this.height + const newestHeight = latest.number + if (newestHeight - currentHeight >= 1) { + this.fetcherDoneFlags.snapTargetHeight = latest.number + this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot + this.fetcherDoneFlags.snapTargetHash = latest.hash() + + // TODO do we need to set this one since it's a duplicate of snapTargetRoot? + this.fetcherDoneFlags.stateRoot = latest.stateRoot + } + } const origin = this.getOrigin(job) const limit = this.getLimit(job) - this.debug(`requested root: ${bytesToHex(this.root)}`) + this.debug(`requested root: ${bytesToHex(this.fetcherDoneFlags.snapTargetRoot!)}`) this.debug(`requested origin: ${bytesToHex(origin)}`) this.debug(`requested limit: ${bytesToHex(limit)}`) this.debug( @@ -250,7 +268,7 @@ export class StorageFetcher extends Fetcher req.accountHash), origin, limit, @@ -617,10 +635,6 @@ export class StorageFetcher extends Fetcher stateManager?: DefaultStateManager @@ -70,6 +71,7 @@ type NodeRequestData = { export class TrieNodeFetcher extends Fetcher { protected debug: Debugger root: Uint8Array + height: bigint stateManager: DefaultStateManager fetcherDoneFlags: SnapFetcherDoneFlags @@ -102,6 +104,7 @@ export class TrieNodeFetcher extends Fetcher constructor(options: TrieNodeFetcherOptions) { super(options) this.root = options.root + this.height = options.height this.fetcherDoneFlags = options.fetcherDoneFlags ?? getInitFecherDoneFlags() this.pathToNodeRequestData = new OrderedMap() this.requestedNodeToPath = new Map() @@ -119,7 +122,7 @@ export class TrieNodeFetcher extends Fetcher // will always start with root node as first set of node requests this.pathToNodeRequestData.setElement('', { requested: false, - nodeHash: bytesToHex(this.root), + nodeHash: bytesToHex(this.fetcherDoneFlags.snapTargetRoot!), nodeParentHash: '', // root node does not have a parent } as NodeRequestData) @@ -149,12 +152,24 @@ export class TrieNodeFetcher extends Fetcher // TODOs: // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool - await peer?.latest() + const latest = await peer?.latest() + if (latest !== undefined && latest.stateRoot !== undefined) { + const currentHeight = this.height + const newestHeight = latest.number + if (newestHeight - currentHeight >= 1) { + this.fetcherDoneFlags.snapTargetHeight = latest.number + this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot + this.fetcherDoneFlags.snapTargetHash = latest.hash() + + // TODO do we need to set this one since it's a duplicate of snapTargetRoot? + this.fetcherDoneFlags.stateRoot = latest.stateRoot + } + } const { paths, pathStrings } = task const rangeResult = await peer!.snap!.getTrieNodes({ - root: this.root, + root: this.fetcherDoneFlags.snapTargetRoot!, paths, bytes: BigInt(this.config.maxRangeBytes), }) @@ -385,7 +400,7 @@ export class TrieNodeFetcher extends Fetcher this.debug( `Stored accountTrie with root actual=${bytesToHex( this.accountTrie.root() - )} expected=${bytesToHex(this.root)}` + )} expected=${bytesToHex(this.fetcherDoneFlags.snapTargetRoot!)}` ) } } catch (e) { @@ -447,10 +462,6 @@ export class TrieNodeFetcher extends Fetcher return tasks } - updateStateRoot(stateRoot: Uint8Array) { - this.root = stateRoot - } - nextTasks(): void { try { if (this.in.length === 0) { diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 69162ed194..00790ea9b7 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -230,6 +230,7 @@ export class SnapSynchronizer extends Synchronizer { pool: this.pool, stateManager: this.execution.vm.stateManager as DefaultStateManager, root: stateRoot, + height: height, // This needs to be determined from the current state of the MPT dump first: BigInt(0), fetcherDoneFlags: this.fetcherDoneFlags, From b6c02c41b2e6f1f21477894c28efeb65dc59b483 Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 26 Jul 2024 10:41:15 -0700 Subject: [PATCH 31/40] Remove root field from fetcher classes that use the root in the flags object --- packages/client/src/sync/fetcher/accountfetcher.ts | 6 +----- packages/client/src/sync/fetcher/storagefetcher.ts | 6 +----- packages/client/src/sync/fetcher/trienodefetcher.ts | 5 ++--- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 519ab58c85..4e07fcfc27 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -43,9 +43,7 @@ type AccountDataResponse = AccountData[] & { completed?: boolean } * @memberof module:sync/fetcher */ export interface AccountFetcherOptions extends FetcherOptions { - /** Root hash of the account trie to serve */ - root: Uint8Array - + // height of block being targeted for snap sync height: bigint /** The origin to start account fetcher from (including), by default starts from 0 (0x0000...) */ @@ -74,7 +72,6 @@ export class AccountFetcher extends Fetcher stateManager: DefaultStateManager accountTrie: Trie - root: Uint8Array height: bigint highestKnownHash: Uint8Array | undefined @@ -95,7 +92,6 @@ export class AccountFetcher extends Fetcher super(options) this.fetcherDoneFlags = options.fetcherDoneFlags ?? getInitFecherDoneFlags() - this.root = options.root this.height = options.height this.first = options.first this.count = options.count ?? BIGINT_2EXP256 - this.first diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index bc3be98f48..4caba9b3d9 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -42,9 +42,7 @@ export type StorageRequest = { * @memberof module:sync/fetcher */ export interface StorageFetcherOptions extends FetcherOptions { - /** Root hash of the account trie to serve */ - root: Uint8Array - + // height of block being targeted for snap sync height: bigint /** Storage requests to fetch */ @@ -71,7 +69,6 @@ export type JobTask = { export class StorageFetcher extends Fetcher { protected debug: Debugger - root: Uint8Array height: bigint stateManager: DefaultStateManager fetcherDoneFlags: SnapFetcherDoneFlags @@ -91,7 +88,6 @@ export class StorageFetcher extends Fetcher stateManager?: DefaultStateManager @@ -70,7 +69,8 @@ type NodeRequestData = { export class TrieNodeFetcher extends Fetcher { protected debug: Debugger - root: Uint8Array + + // height of block being targeted for snap sync height: bigint stateManager: DefaultStateManager @@ -103,7 +103,6 @@ export class TrieNodeFetcher extends Fetcher */ constructor(options: TrieNodeFetcherOptions) { super(options) - this.root = options.root this.height = options.height this.fetcherDoneFlags = options.fetcherDoneFlags ?? getInitFecherDoneFlags() this.pathToNodeRequestData = new OrderedMap() From 1cd93152b95a62e3b47358f8df2d115b796fb9e7 Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 26 Jul 2024 11:12:25 -0700 Subject: [PATCH 32/40] Remove unused parameter inputs --- packages/client/src/sync/fetcher/accountfetcher.ts | 2 -- packages/client/src/sync/snapsync.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 4e07fcfc27..ab4252fa35 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -104,7 +104,6 @@ export class AccountFetcher extends Fetcher this.storageFetcher = new StorageFetcher({ config: this.config, pool: this.pool, - root: this.fetcherDoneFlags.snapTargetRoot!, height: this.height, storageRequests: [], first: BIGINT_1, @@ -123,7 +122,6 @@ export class AccountFetcher extends Fetcher this.trieNodeFetcher = new TrieNodeFetcher({ config: this.config, pool: this.pool, - root: this.fetcherDoneFlags.snapTargetRoot!, height: this.height, stateManager: this.stateManager, destroyWhenDone: false, diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 00790ea9b7..291b3a8265 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -229,7 +229,6 @@ export class SnapSynchronizer extends Synchronizer { config: this.config, pool: this.pool, stateManager: this.execution.vm.stateManager as DefaultStateManager, - root: stateRoot, height: height, // This needs to be determined from the current state of the MPT dump first: BigInt(0), From 9c8495f2bf68e4af74ab05dc66bbcf3dcb245908 Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 26 Jul 2024 11:58:05 -0700 Subject: [PATCH 33/40] Fix import and usage of proof function --- packages/client/src/sync/fetcher/accountfetcher.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 5917f8fe7d..71c7e29984 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -33,7 +33,7 @@ import type { AccountData } from '../../net/protocol/snapprotocol.js' import type { FetcherOptions } from './fetcher.js' import type { StorageRequest } from './storagefetcher.js' import type { Job, SnapFetcherDoneFlags } from './types.js' -import { Trie } from '@ethereumjs/trie' +import type { Trie } from '@ethereumjs/trie' import type { Debugger } from 'debug' type AccountDataResponse = AccountData[] & { completed?: boolean } @@ -425,7 +425,7 @@ export class AccountFetcher extends Fetcher // check zero-element proof if (rangeResult.proof.length > 0) { try { - const isMissingRightRange = await Trie.verifyRangeProof( + const isMissingRightRange = await verifyTrieRangeProof( this.fetcherDoneFlags.snapTargetRoot!, origin, null, From 20db364c4938b25d354c22f444969447b48731b7 Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 29 Jul 2024 10:52:36 -0700 Subject: [PATCH 34/40] Do not set fetcherDoneFlags.stateRoot on FCU root updates --- packages/client/src/sync/fetcher/accountfetcher.ts | 3 --- packages/client/src/sync/fetcher/storagefetcher.ts | 3 --- packages/client/src/sync/fetcher/trienodefetcher.ts | 3 --- 3 files changed, 9 deletions(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 71c7e29984..5d70faa378 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -394,9 +394,6 @@ export class AccountFetcher extends Fetcher this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() - - // TODO do we need to set this one since it's a duplicate of snapTargetRoot? - this.fetcherDoneFlags.stateRoot = latest.stateRoot } } diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index c823b31084..7f9c0ab387 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -231,9 +231,6 @@ export class StorageFetcher extends Fetcher this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() - - // TODO do we need to set this one since it's a duplicate of snapTargetRoot? - this.fetcherDoneFlags.stateRoot = latest.stateRoot } } From edd086615e62ffe24c09b613412fe663e0ab0c8c Mon Sep 17 00:00:00 2001 From: Amir Date: Mon, 29 Jul 2024 14:12:43 -0700 Subject: [PATCH 35/40] Update storageFetcher to track root and height of each request created on a more granular level --- .../client/src/sync/fetcher/storagefetcher.ts | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index 7f9c0ab387..0cc4556083 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -26,6 +26,7 @@ import type { Job, SnapFetcherDoneFlags } from './types.js' import type { Debugger } from 'debug' const TOTAL_RANGE_END = BIGINT_2 ** BIGINT_256 - BIGINT_1 +const LOOKBACK_WINDOW = 1 type StorageDataResponse = StorageData[][] & { completed?: boolean } @@ -34,6 +35,8 @@ export type StorageRequest = { storageRoot: Uint8Array first: bigint count: bigint + stateRoot: Uint8Array // needed for verifying request if targetRoot has been updated since the creation of this request + height: bigint // needed for checking if request is still safely within stateRoot-lookback-window before requesting to avoid being banned by peer } /** @@ -64,6 +67,8 @@ export interface StorageFetcherOptions extends FetcherOptions { export type JobTask = { storageRequests: StorageRequest[] multi: boolean + stateRoot: Uint8Array // needed for verifying request if targetRoot has been updated since the creation of this request + height: bigint // needed for checking if request is still safely within stateRoot-lookback-window before requesting to avoid being banned by peer } export class StorageFetcher extends Fetcher { @@ -227,7 +232,7 @@ export class StorageFetcher extends Fetcher= 1) { + if (newestHeight - currentHeight >= LOOKBACK_WINDOW) { this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() @@ -259,8 +264,17 @@ export class StorageFetcher extends Fetcher= + LOOKBACK_WINDOW + ) { + // skip request if we are close to being outside of lookback range to avoid getting banned + this.debug(`skipping request that is close to being outside of lookback range`) + return Object.assign([], [{ skipped: true }], { completed: true }) + } + const rangeResult = await peer!.snap!.getStorageRanges({ - root: this.fetcherDoneFlags.snapTargetRoot!, + root: task.stateRoot, accounts: task.storageRequests.map((req) => req.accountHash), origin, limit, @@ -398,6 +412,8 @@ export class StorageFetcher extends Fetcher Date: Tue, 30 Jul 2024 13:46:44 -0700 Subject: [PATCH 36/40] Add constant for lookback window and debug --- packages/client/src/config.ts | 4 +++ .../client/src/sync/fetcher/accountfetcher.ts | 3 +- .../client/src/sync/fetcher/storagefetcher.ts | 5 ++-- .../src/sync/fetcher/trienodefetcher.ts | 30 +++++++++++++++++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/client/src/config.ts b/packages/client/src/config.ts index 74fa734339..1d614dc9dd 100644 --- a/packages/client/src/config.ts +++ b/packages/client/src/config.ts @@ -326,6 +326,7 @@ export interface ConfigOptions { pruneEngineCache?: boolean snapAvailabilityDepth?: bigint snapTransitionSafeDepth?: bigint + snapLookbackWindow?: number /** * Save account keys preimages in the meta db (default: false) @@ -393,6 +394,7 @@ export class Config { // distance from head at which we can safely transition from a synced snapstate to vmexecution // randomly kept it at 5 for fast testing purposes but ideally should be >=32 slots public static readonly SNAP_TRANSITION_SAFE_DEPTH = BigInt(5) + public static readonly SNAP_LOOKBACK_WINDOW = 2 public readonly logger: Logger public readonly syncmode: SyncMode @@ -441,6 +443,7 @@ export class Config { public readonly engineNewpayloadMaxTxsExecute: number public readonly snapAvailabilityDepth: bigint public readonly snapTransitionSafeDepth: bigint + public readonly snapLookbackWindow: number public readonly prefixStorageTrieKeys: boolean // Defaulting to false as experimental as of now @@ -535,6 +538,7 @@ export class Config { this.snapAvailabilityDepth = options.snapAvailabilityDepth ?? Config.SNAP_AVAILABILITY_DEPTH this.snapTransitionSafeDepth = options.snapTransitionSafeDepth ?? Config.SNAP_TRANSITION_SAFE_DEPTH + this.snapLookbackWindow = options.snapLookbackWindow ?? Config.SNAP_LOOKBACK_WINDOW this.prefixStorageTrieKeys = options.prefixStorageTrieKeys ?? true this.enableSnapSync = options.enableSnapSync ?? false diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 5d70faa378..e11d2a39e6 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -390,10 +390,11 @@ export class AccountFetcher extends Fetcher // could be running independently of eachother in some cases const currentHeight = this.height const newestHeight = latest.number - if (newestHeight - currentHeight >= 1) { + if (newestHeight - currentHeight >= this.config.snapLookbackWindow) { this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() + this.height = newestHeight } } diff --git a/packages/client/src/sync/fetcher/storagefetcher.ts b/packages/client/src/sync/fetcher/storagefetcher.ts index 0cc4556083..c3c4c60dd7 100644 --- a/packages/client/src/sync/fetcher/storagefetcher.ts +++ b/packages/client/src/sync/fetcher/storagefetcher.ts @@ -26,7 +26,6 @@ import type { Job, SnapFetcherDoneFlags } from './types.js' import type { Debugger } from 'debug' const TOTAL_RANGE_END = BIGINT_2 ** BIGINT_256 - BIGINT_1 -const LOOKBACK_WINDOW = 1 type StorageDataResponse = StorageData[][] & { completed?: boolean } @@ -232,7 +231,7 @@ export class StorageFetcher extends Fetcher= LOOKBACK_WINDOW) { + if (newestHeight - currentHeight >= this.config.snapLookbackWindow) { this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() @@ -266,7 +265,7 @@ export class StorageFetcher extends Fetcher= - LOOKBACK_WINDOW + this.config.snapLookbackWindow ) { // skip request if we are close to being outside of lookback range to avoid getting banned this.debug(`skipping request that is close to being outside of lookback range`) diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index 88c0776b88..a143ffe06d 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -120,7 +120,7 @@ export class TrieNodeFetcher extends Fetcher // will always start with root node as first set of node requests this.pathToNodeRequestData.setElement('', { requested: false, - nodeHash: bytesToHex(this.fetcherDoneFlags.snapTargetRoot!), + nodeHash: bytesToHex(this.fetcherDoneFlags.snapTargetRoot!), // TODO this target could change, have to update tfn to clear and recreate root request on targetRoot changing nodeParentHash: '', // root node does not have a parent } as NodeRequestData) @@ -154,10 +154,25 @@ export class TrieNodeFetcher extends Fetcher if (latest !== undefined && latest.stateRoot !== undefined) { const currentHeight = this.height const newestHeight = latest.number - if (newestHeight - currentHeight >= 1) { + if (newestHeight - currentHeight >= this.config.snapLookbackWindow) { + this.debug('dbg810: updating roots') + // update latest height and root this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() + this.height = newestHeight + + // clear any tasks or requests and create new request for newest root set to + this.clear() + this.pathToNodeRequestData = new OrderedMap() + this.requestedNodeToPath = new Map() + this.fetchedAccountNodes = new Map() + this.nodeCount = 0 + this.pathToNodeRequestData.setElement('', { + requested: false, + nodeHash: bytesToHex(this.fetcherDoneFlags.snapTargetRoot!), // TODO this target could change, have to update tfn to clear and recreate root request on targetRoot changing + nodeParentHash: '', // root node does not have a parent + } as NodeRequestData) } } @@ -169,6 +184,14 @@ export class TrieNodeFetcher extends Fetcher bytes: BigInt(this.config.maxRangeBytes), }) + this.debug(`dbg800: trienodefetcher.ts`) + this.debug(`root: ${bytesToHex(this.fetcherDoneFlags.snapTargetRoot!)}`) + this.debug( + `paths requested: ${paths.map((valArr, _) => { + return valArr.map((val, _) => bytesToHex(val)) + })}`, + ) + // Response is valid, but check if peer is signalling that it does not have // the requested data. For trie node range queries that means the peer is not // yet synced. @@ -237,6 +260,9 @@ export class TrieNodeFetcher extends Fetcher let unknownChildNodeCount = 0 let hasStorageComponent = false + this.debug(`dbg804: trienodefetcher.ts`) + this.debug(`nodeData: ${node}`) + // get all children of received node if (node instanceof BranchNode) { const children = (node as BranchNode).getChildren() From 3e8a1e49cc1b878a5500c0c532cbe915dc50e826 Mon Sep 17 00:00:00 2001 From: harkamal Date: Fri, 6 Sep 2024 22:30:52 +0530 Subject: [PATCH 37/40] debug, fix and get the snapsync sim working on dencun --- packages/blockchain/src/blockchain.ts | 8 +++ packages/client/src/net/server/rlpxserver.ts | 2 +- packages/client/src/sync/snapsync.ts | 2 +- packages/client/test/sim/configs/mainnet.json | 1 + packages/client/test/sim/simutils.ts | 53 +++++++++++++++---- packages/client/test/sim/single-run.sh | 2 +- packages/client/test/sim/snapsync.md | 2 +- packages/client/test/sim/snapsync.spec.ts | 9 +++- 8 files changed, 64 insertions(+), 15 deletions(-) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index a9a45d740e..e2f6de1079 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -1272,6 +1272,13 @@ export class Blockchain implements BlockchainInterface { number: 0, stateRoot, withdrawalsRoot: common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined, + ...(common.isActivatedEIP(4844) + ? { + blobGasUsed: 0, + excessBlobGas: 0, + parentBeaconBlockRoot: new Uint8Array(32), + } + : undefined), } if (common.consensusType() === 'poa') { if (common.genesis().extraData) { @@ -1282,6 +1289,7 @@ export class Blockchain implements BlockchainInterface { header.extraData = concatBytes(new Uint8Array(32), new Uint8Array(65)) } } + return createBlockFromBlockData( { header, withdrawals: common.isActivatedEIP(4895) ? [] : undefined }, { common }, diff --git a/packages/client/src/net/server/rlpxserver.ts b/packages/client/src/net/server/rlpxserver.ts index f041ff7a5c..34fdcdff28 100644 --- a/packages/client/src/net/server/rlpxserver.ts +++ b/packages/client/src/net/server/rlpxserver.ts @@ -65,7 +65,7 @@ export class RlpxServer extends Server { super(options) // As of now, the devp2p dpt server listens on the ip4 protocol by default and hence the ip in the // bootnode needs to be of ip4 by default - this.ip = options.config.extIP ?? '0.0.0.0' + this.ip = options.config.extIP ?? '127.0.0.1' this.discovery = options.config.discV4 || options.config.discDns this.clientFilter = options.clientFilter ?? [ 'go1.5', diff --git a/packages/client/src/sync/snapsync.ts b/packages/client/src/sync/snapsync.ts index 7e5ff092fd..92a4add023 100644 --- a/packages/client/src/sync/snapsync.ts +++ b/packages/client/src/sync/snapsync.ts @@ -229,7 +229,7 @@ export class SnapSynchronizer extends Synchronizer { config: this.config, pool: this.pool, stateManager: this.execution.vm.stateManager as DefaultStateManager, - height: height, + height, // This needs to be determined from the current state of the MPT dump first: BigInt(0), fetcherDoneFlags: this.fetcherDoneFlags, diff --git a/packages/client/test/sim/configs/mainnet.json b/packages/client/test/sim/configs/mainnet.json index 321a864e18..d58d77ed2c 100644 --- a/packages/client/test/sim/configs/mainnet.json +++ b/packages/client/test/sim/configs/mainnet.json @@ -13,6 +13,7 @@ "londonBlock": 0, "mergeForkBlock": 0, "shanghaiTime": 0, + "cancunTime": 0, "terminalTotalDifficulty": 1, "terminalTotalDifficultyPassed": true }, diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 2ddba64f10..cd25699ecc 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -488,19 +488,48 @@ export async function setupEngineUpdateRelay(client: EthereumClient, peerBeaconU } } - const playUpdate = async (payload: any, finalizedBlockHash: string, version: string) => { - if (version !== 'capella') { - throw Error('only capella replay supported yet') - } + const playUpdate = async ( + payload: any, + { + finalizedBlockHash, + parentBeaconBlockRoot, + blobVersionedHashes, + }: { + finalizedBlockHash: string + parentBeaconBlockRoot?: string + blobVersionedHashes?: string[] + }, + version: string, + ) => { + let newPayloadParams, newPayloadMethod, fcuMethod const fcUState = { headBlockHash: payload.blockHash, safeBlockHash: finalizedBlockHash, finalizedBlockHash, } + + if (version === 'capella') { + newPayloadParams = [payload] + newPayloadMethod = 'engine_newPayloadV2' + fcuMethod = 'engine_forkchoiceUpdatedV2' + } else if (version === 'deneb') { + if (parentBeaconBlockRoot === undefined || blobVersionedHashes === undefined) { + throw Error( + `parentBeaconBlockRoot=undefined or blobVersionedHashes=undefined for CL fork=${version}`, + ) + } + + newPayloadMethod = 'engine_newPayloadV3' + fcuMethod = 'engine_forkchoiceUpdatedV3' + newPayloadParams = [payload, blobVersionedHashes, parentBeaconBlockRoot] + } else { + throw Error(`playUpdate not implemented for CL fork=${version}`) + } + console.log('playUpdate', fcUState) try { - const newPayloadRes = await engineMethods['engine_newPayloadV2']([payload]) + const newPayloadRes = await engineMethods[newPayloadMethod](newPayloadParams) if ( newPayloadRes.status === undefined || !['SYNCING', 'VALID', 'ACCEPTED'].includes(newPayloadRes.status) @@ -510,7 +539,7 @@ export async function setupEngineUpdateRelay(client: EthereumClient, peerBeaconU ) } - const fcuRes = await engineMethods['engine_forkchoiceUpdatedV2']([fcUState]) + const fcuRes = await engineMethods[fcuMethod]([fcUState]) if ( fcuRes.payloadStatus === undefined || !['SYNCING', 'VALID', 'ACCEPTED'].includes(newPayloadRes.status) @@ -543,13 +572,19 @@ export async function setupEngineUpdateRelay(client: EthereumClient, peerBeaconU } const beaconHead = await (await fetch(`${peerBeaconUrl}/eth/v2/beacon/blocks/head`)).json() - const payload = executionPayloadFromBeaconPayload( beaconHead.data.message.body.execution_payload, ) const finalizedBlockHash = beaconFinalized.data.finalized_header.execution.block_hash - - await playUpdate(payload, finalizedBlockHash, beaconHead.version) + const parentBeaconBlockRoot = beaconHead.data.message.parent_root + // blobVersionedHashes should be correctly calculated from commitments + const blobVersionedHashes: string[] = [] + + await playUpdate( + payload, + { finalizedBlockHash, parentBeaconBlockRoot, blobVersionedHashes }, + beaconHead.version, + ) } catch (e) { console.log('update fetch error', e) updateState('ERRORED') diff --git a/packages/client/test/sim/single-run.sh b/packages/client/test/sim/single-run.sh index 50b5852f08..92cc6e069b 100755 --- a/packages/client/test/sim/single-run.sh +++ b/packages/client/test/sim/single-run.sh @@ -39,7 +39,7 @@ then echo "geth requires NETWORKID to be passed in env, exiting..." exit; fi; - ELCLIENT_IMAGE="ethereum/client-go:v1.12.2" + ELCLIENT_IMAGE="ethereum/client-go:v1.14.8" echo "ELCLIENT=$ELCLIENT using ELCLIENT_IMAGE=$ELCLIENT_IMAGE NETWORKID=$NETWORKID" ;; *) diff --git a/packages/client/test/sim/snapsync.md b/packages/client/test/sim/snapsync.md index ce71305420..be4284a000 100644 --- a/packages/client/test/sim/snapsync.md +++ b/packages/client/test/sim/snapsync.md @@ -16,7 +16,7 @@ Note: All commands should be run from the `client` package directory root (so so 1. Start external geth client: ```bash -NETWORK=mainnet NETWORKID=1337903 ELCLIENT=geth EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0" DATADIR=/usr/app/ethereumjs/packages/client/data test/sim/single-run.sh +NETWORK=mainnet NETWORKID=1337903 ELCLIENT=geth EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" DATADIR=/usr/app/ethereumjs/packages/client/data test/sim/single-run.sh ``` 2. (optional) Add some txs/state to geth diff --git a/packages/client/test/sim/snapsync.spec.ts b/packages/client/test/sim/snapsync.spec.ts index 89ae5e2422..7b2269f853 100644 --- a/packages/client/test/sim/snapsync.spec.ts +++ b/packages/client/test/sim/snapsync.spec.ts @@ -13,6 +13,7 @@ import { assert, describe, it } from 'vitest' import { Config } from '../../src/config.js' import { getLogger } from '../../src/logging.js' import { Event } from '../../src/types.js' +import { parseMultiaddrs } from '../../src/util/index.js' import { createInlineClient, @@ -55,7 +56,7 @@ export async function runTx(data: PrefixedHexString | '', to?: PrefixedHexString describe('simple mainnet test run', async () => { if (process.env.EXTRA_CL_PARAMS === undefined) { - process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0' + process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0' } // Better add it as a option in startnetwork process.env.NETWORKID = `${common.chainId()}` @@ -74,6 +75,7 @@ describe('simple mainnet test run', async () => { const nodeInfo = (await client.request('admin_nodeInfo', [])).result assert.ok(nodeInfo.enode !== undefined, 'fetched enode for peering') + console.log({ peerEnode: nodeInfo.enode }) console.log(`Waiting for network to start...`) try { @@ -151,7 +153,9 @@ describe('simple mainnet test run', async () => { assert.ok(ejsClient !== null, 'ethereumjs client started') const enode = ejsClient!.server()!.getRlpxInfo().enode + console.log({ enode }) const res = await client.request('admin_addPeer', [enode]) + console.log(res) assert.equal(res.result, true, 'successfully requested Geth add EthereumJS as peer') const peerConnectTimeout = new Promise((_resolve, reject) => setTimeout(reject, 10000)) @@ -159,6 +163,7 @@ describe('simple mainnet test run', async () => { await Promise.race([peerConnectedPromise, peerConnectTimeout]) assert.ok(true, 'connected to geth peer') } catch (e) { + console.log(e) assert.fail('could not connect to geth peer in 10 seconds') } }, @@ -251,7 +256,7 @@ async function createSnapClient( const logger = getLogger({ logLevel: 'debug' }) const config = new Config({ common, - bootnodes, + bootnodes: parseMultiaddrs(bootnodes), multiaddrs: [], logger, accountCache: 10000, From 9a3b14cf3126bd17fb26d3130f6f08edc6fa5324 Mon Sep 17 00:00:00 2001 From: Amir Date: Thu, 12 Sep 2024 18:29:28 -0700 Subject: [PATCH 38/40] Add heal cycle repeat flag --- packages/client/src/config.ts | 2 +- .../client/src/sync/fetcher/accountfetcher.ts | 4 ++++ .../client/src/sync/fetcher/trienodefetcher.ts | 17 ++++++++++++++--- packages/client/src/sync/fetcher/types.ts | 4 ++++ 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/client/src/config.ts b/packages/client/src/config.ts index 1d614dc9dd..e6a5975107 100644 --- a/packages/client/src/config.ts +++ b/packages/client/src/config.ts @@ -394,7 +394,7 @@ export class Config { // distance from head at which we can safely transition from a synced snapstate to vmexecution // randomly kept it at 5 for fast testing purposes but ideally should be >=32 slots public static readonly SNAP_TRANSITION_SAFE_DEPTH = BigInt(5) - public static readonly SNAP_LOOKBACK_WINDOW = 2 + public static readonly SNAP_LOOKBACK_WINDOW = 1 public readonly logger: Logger public readonly syncmode: SyncMode diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index e11d2a39e6..5ca7b10cb9 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -278,6 +278,10 @@ export class AccountFetcher extends Fetcher case TrieNodeFetcher: // TODO update to check if heal phase completed successfully and then continue with next // healing phase + if (fetcherDoneFlags.trieNodeFetcher.healCycleRepeatCount > 0) { + fetcherDoneFlags.trieNodeFetcher.healCycleRepeatCount-- + break + } fetcherDoneFlags.trieNodeFetcher.done = true break } diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index a143ffe06d..7663ebfbfb 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -102,7 +102,6 @@ export class TrieNodeFetcher extends Fetcher */ constructor(options: TrieNodeFetcherOptions) { super(options) - this.height = options.height this.fetcherDoneFlags = options.fetcherDoneFlags ?? getInitFecherDoneFlags() this.pathToNodeRequestData = new OrderedMap() this.requestedNodeToPath = new Map() @@ -151,16 +150,27 @@ export class TrieNodeFetcher extends Fetcher // 1. Properly rewrite Fetcher with async/await -> allow to at least place in Fetcher.next() // 2. Properly implement ETH request IDs -> allow to call on non-idle in Peer Pool const latest = await peer?.latest() + // console.log('dbg850') if (latest !== undefined && latest.stateRoot !== undefined) { - const currentHeight = this.height + const currentHeight = this.fetcherDoneFlags.trieNodeFetcher.currentHeight const newestHeight = latest.number + // console.log(currentHeight) + // console.log(newestHeight) + // console.log(this.config.snapLookbackWindow) + // console.log(newestHeight - currentHeight >= this.config.snapLookbackWindow) if (newestHeight - currentHeight >= this.config.snapLookbackWindow) { this.debug('dbg810: updating roots') // update latest height and root this.fetcherDoneFlags.snapTargetHeight = latest.number this.fetcherDoneFlags.snapTargetRoot = latest.stateRoot this.fetcherDoneFlags.snapTargetHash = latest.hash() - this.height = newestHeight + this.fetcherDoneFlags.trieNodeFetcher.currentHeight = newestHeight + + // console.log('dbg811') + // console.log(latest.number) + // console.log(bytesToHex(latest.stateRoot)) + // console.log(bytesToHex(latest.hash())) + // console.log(latest.toJSON()) // clear any tasks or requests and create new request for newest root set to this.clear() @@ -332,6 +342,7 @@ export class TrieNodeFetcher extends Fetcher await this.accountTrie.lookupNode(childNode.nodeHash as Uint8Array) } } catch (e) { + // console.log('dbg901') // if error is thrown, than the node is unknown and should be queued for fetching unknownChildNodeCount++ const { parentAccountHash } = this.pathToNodeRequestData.getElementByKey( diff --git a/packages/client/src/sync/fetcher/types.ts b/packages/client/src/sync/fetcher/types.ts index 8004360450..bfbde0cd7e 100644 --- a/packages/client/src/sync/fetcher/types.ts +++ b/packages/client/src/sync/fetcher/types.ts @@ -39,6 +39,8 @@ export type SnapFetcherDoneFlags = { first: bigint count: bigint done: boolean + healCycleRepeatCount: number // used in AccountFetcher to determine how many times to repeat heal cycles + currentHeight: bigint } stateRoot?: Uint8Array } @@ -70,6 +72,8 @@ export function getInitFecherDoneFlags(): SnapFetcherDoneFlags { first: BigInt(0), count: BigInt(0), done: false, + healCycleRepeatCount: Number(process.env.HEAL_CYCLE_REPEATS ?? '0'), + currentHeight: BigInt(0), }, } } From 939582938525bece4d4bc02400e86db389cc51c8 Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 13 Sep 2024 12:52:51 -0700 Subject: [PATCH 39/40] Make repeats accurate --- packages/client/src/sync/fetcher/accountfetcher.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/sync/fetcher/accountfetcher.ts b/packages/client/src/sync/fetcher/accountfetcher.ts index 5ca7b10cb9..147ea07bfe 100644 --- a/packages/client/src/sync/fetcher/accountfetcher.ts +++ b/packages/client/src/sync/fetcher/accountfetcher.ts @@ -278,7 +278,7 @@ export class AccountFetcher extends Fetcher case TrieNodeFetcher: // TODO update to check if heal phase completed successfully and then continue with next // healing phase - if (fetcherDoneFlags.trieNodeFetcher.healCycleRepeatCount > 0) { + if (fetcherDoneFlags.trieNodeFetcher.healCycleRepeatCount > 1) { fetcherDoneFlags.trieNodeFetcher.healCycleRepeatCount-- break } From a73b65974f3477daef756f67d8d556fa27ed6bca Mon Sep 17 00:00:00 2001 From: Amir Date: Fri, 13 Sep 2024 12:53:02 -0700 Subject: [PATCH 40/40] Remove unused field --- packages/client/src/sync/fetcher/trienodefetcher.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/client/src/sync/fetcher/trienodefetcher.ts b/packages/client/src/sync/fetcher/trienodefetcher.ts index 7663ebfbfb..ef41fbc49b 100644 --- a/packages/client/src/sync/fetcher/trienodefetcher.ts +++ b/packages/client/src/sync/fetcher/trienodefetcher.ts @@ -69,9 +69,6 @@ type NodeRequestData = { export class TrieNodeFetcher extends Fetcher { protected debug: Debugger - // height of block being targeted for snap sync - height: bigint - stateManager: DefaultStateManager fetcherDoneFlags: SnapFetcherDoneFlags accountTrie: Trie