Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snap sync moving root/healing implementation #3332

Open
wants to merge 56 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ff41a35
Add updateRoot method to fetchers
Mar 22, 2024
6c00e9c
Update the fetcher roots in syncWithPeer if fetcher already exists
Mar 22, 2024
74f0adc
Merge branch 'master' into snap-sync-moving-root
scorbajio Mar 22, 2024
8990b1f
Merge branch 'master' of https://github.com/ethereumjs/ethereumjs-mon…
Mar 27, 2024
dd21e73
Update fetcher roots on FCUs
Apr 4, 2024
9932c21
Merge branch 'master' of https://github.com/ethereumjs/ethereumjs-mon…
Apr 4, 2024
628135e
Merge branch 'snap-sync-moving-root' of https://github.com/ethereumjs…
Apr 4, 2024
19adeea
Call syncWithPeer directly from buildHeadState for updating root
Apr 5, 2024
1e1b1cc
Keep track of requested pathStrings to remove duplicate requests in T…
Apr 9, 2024
92ecef8
Merge branch 'master' into snap-sync-moving-root
scorbajio Apr 9, 2024
8077f4d
Fix test
Apr 10, 2024
6a2b266
Client -> Peer: add lastEthStatusUpdate property to Peer, set initial…
holgerd77 Apr 11, 2024
4dd9bf8
Redo handshake in client peer pool periodically, add force option to …
holgerd77 Apr 11, 2024
e268f9a
Some basic refinement of peer pool periodic request logic (refactor p…
holgerd77 Apr 15, 2024
b053277
Client: consolidate latest() method in base Synchronizer class
holgerd77 Apr 15, 2024
72a28d1
Client: add preparatory updatedBestHeader to Peer, refactor latest() …
holgerd77 Apr 15, 2024
f29c3c6
Client: add potential best header num differentiated logic to Peer, l…
holgerd77 Apr 15, 2024
6ea23b0
Client: add Fetcher hack to call peer.latest()
holgerd77 Apr 15, 2024
fabf53c
Various cleanups
holgerd77 Apr 15, 2024
ccf9366
Client: Fix lightsync.spec.ts tests
holgerd77 Apr 16, 2024
4cd01aa
Some clean-ups
holgerd77 Apr 16, 2024
5777c42
Client: Fix beaconsync.spec.ts tests
holgerd77 Apr 16, 2024
9ea28d8
Client: Fix fullsync.spec.tst tests
holgerd77 Apr 16, 2024
9a6ae27
Client: Fix snapsync.spec.ts tests
holgerd77 Apr 16, 2024
5c42d73
Client: Fix fetcher tests
holgerd77 Apr 16, 2024
9e60a09
Client: Fix eth syncing.spec.ts tests
holgerd77 Apr 16, 2024
d6c2a6c
Client: Fix integration tests
holgerd77 Apr 16, 2024
58cb2b4
Client: Backup lightsync integration tests (lightsync not supported a…
holgerd77 Apr 16, 2024
c53fa4b
Lightsync tests deprecation note
holgerd77 Apr 16, 2024
20bf36f
Merge remote-tracking branch 'origin/master' into client-add-mechanis…
acolytec3 Apr 17, 2024
fe9a4ec
Make lightsync tests run (but fail)
acolytec3 Apr 17, 2024
0117083
Merge branch 'master' into client-add-mechanism-to-redo-peer-handshak…
scorbajio Apr 18, 2024
694211e
Merge branch 'client-add-mechanism-to-redo-peer-handshake-for-status-…
Apr 18, 2024
d8028eb
Receive peer updates only in request function of fetchers
Apr 18, 2024
52d1f6e
Update fetcher roots on new latestHeader
Apr 18, 2024
2f88a6b
Merge branch 'snap-sync-moving-root' of https://github.com/ethereumjs…
Apr 18, 2024
560de58
Merge branch 'master' of https://github.com/ethereumjs/ethereumjs-mon…
Apr 23, 2024
9eb92a8
Merge branch 'master' of https://github.com/ethereumjs/ethereumjs-mon…
Apr 25, 2024
a1da2e6
Merge branch 'master' into snap-sync-moving-root
scorbajio Jun 24, 2024
d469538
Latest changes for integrating updated roots from latest function
scorbajio Jul 9, 2024
6488935
Merge branch 'master' into snap-sync-moving-root
scorbajio Jul 9, 2024
87ee2aa
fix linting issue
scorbajio Jul 9, 2024
d678d20
Merge branch 'snap-sync-moving-root' of github.com:ethereumjs/ethereu…
scorbajio Jul 9, 2024
50fb30b
Move over to getting latest in request function of relevant fetchers
scorbajio Jul 26, 2024
b6c02c4
Remove root field from fetcher classes that use the root in the flags…
scorbajio Jul 26, 2024
1cd9315
Remove unused parameter inputs
scorbajio Jul 26, 2024
591762e
Merge branch 'master' of github.com:ethereumjs/ethereumjs-monorepo in…
scorbajio Jul 26, 2024
9c8495f
Fix import and usage of proof function
scorbajio Jul 26, 2024
20db364
Do not set fetcherDoneFlags.stateRoot on FCU root updates
scorbajio Jul 29, 2024
edd0866
Update storageFetcher to track root and height of each request create…
scorbajio Jul 29, 2024
dca3e3f
Add constant for lookback window and debug
scorbajio Jul 30, 2024
3e8a1e4
debug, fix and get the snapsync sim working on dencun
g11tech Sep 6, 2024
9a3b14c
Add heal cycle repeat flag
scorbajio Sep 13, 2024
9395829
Make repeats accurate
scorbajio Sep 13, 2024
a73b659
Remove unused field
scorbajio Sep 13, 2024
4058a27
Merge branch 'master' of github.com:ethereumjs/ethereumjs-monorepo in…
scorbajio Sep 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion packages/client/src/service/fullethereumservice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -243,7 +244,36 @@ export class FullEthereumService extends Service {
* vm execution
*/
async buildHeadState(): Promise<void> {
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no we don't need stateroot from peer, we already have latest stateroot coming in from CL

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought #3354 was for tracking and accessing the latest root/height from the peer latest function. Are you suggesting passing down the latest root from FCU calls?

}

return
}
this.building = true

try {
Expand Down
8 changes: 8 additions & 0 deletions packages/client/src/sync/fetcher/accountfetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export class AccountFetcher extends Fetcher<JobTask, AccountData[], AccountData>
this.accountTrie = this.stateManager['_getAccountTrie']()

this.debug = createDebugLogger('client:AccountFetcher')
this.debug('dbg101')

this.storageFetcher = new StorageFetcher({
config: this.config,
Expand Down Expand Up @@ -276,6 +277,8 @@ export class AccountFetcher extends Fetcher<JobTask, AccountData[], AccountData>
}
break
case TrieNodeFetcher:
// TODO update to check if heal phase completed successfully and then continue with next
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function with this case would/should anyway be called when heal phase completed successfully

// healing phase
fetcherDoneFlags.trieNodeFetcher.done = true
break
}
Expand Down Expand Up @@ -585,6 +588,11 @@ export class AccountFetcher extends Fetcher<JobTask, AccountData[], AccountData>

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 {
Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/sync/fetcher/storagefetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,10 @@ export class StorageFetcher extends Fetcher<JobTask, StorageData[][], StorageDat
return tasks
}

updateStateRoot(stateRoot: Uint8Array) {
this.root = stateRoot
}

nextTasks(): void {
this.debug(
`Entering nextTasks with primary queue length of ${this.storageRequests.length} and secondary queue length of ${this.fragmentedRequests.length}`
Expand Down
30 changes: 23 additions & 7 deletions packages/client/src/sync/fetcher/trienodefetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type FetchedNodeData = {
}

type NodeRequestData = {
requested: boolean
nodeHash: string
nodeParentHash: string
parentAccountHash?: string // for leaf account nodes that contain a storage component
Expand Down Expand Up @@ -117,6 +118,7 @@ export class TrieNodeFetcher extends Fetcher<JobTask, Uint8Array[], Uint8Array>

// 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)
Expand Down Expand Up @@ -263,6 +265,7 @@ export class TrieNodeFetcher extends Fetcher<JobTask, Uint8Array[], Uint8Array>
storagePath,
].join('/')
this.pathToNodeRequestData.setElement(syncPath, {
requested: false,
nodeHash: bytesToHex(storageRoot),
nodeParentHash: nodeHash,
parentAccountHash: nodeHash,
Expand Down Expand Up @@ -299,6 +302,7 @@ export class TrieNodeFetcher extends Fetcher<JobTask, Uint8Array[], Uint8Array>
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,
Expand Down Expand Up @@ -411,17 +415,25 @@ export class TrieNodeFetcher extends Fetcher<JobTask, Uint8Array[], Uint8Array>
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}`)
Expand All @@ -435,6 +447,10 @@ export class TrieNodeFetcher extends Fetcher<JobTask, Uint8Array[], Uint8Array>
return tasks
}

updateStateRoot(stateRoot: Uint8Array) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to see that the root is not continually updated.

so the thing is: you start trie node fetcher with latest root, keep requesting data till peers give you, if they start replying with empty, then we restart the entire fetcher with the new root.

so this is to be implemented as a while loop in the account fetcher's fetch where we start and run the trie node fetcher (either fetcher resolves or errors with error "out of range root", which will cause account fetcher to spin a new trie node fetcher with new root)

this.root = stateRoot
}

nextTasks(): void {
try {
if (this.in.length === 0) {
Expand Down
1 change: 1 addition & 0 deletions packages/client/test/sync/fetcher/trienodefetcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ describe('[TrieNodeFetcher]', async () => {

fetcher.pathToNodeRequestData = new OrderedMap<string, any>()
fetcher.pathToNodeRequestData.setElement('', {
requested: false,
nodeHash: bytesToHex(new Uint8Array(0)),
nodeParentHash: '',
})
Expand Down
Loading