Skip to content

Commit

Permalink
client: integrate snapsync on experimental basis (#3031)
Browse files Browse the repository at this point in the history
* client: integrate snapsync on experimental basis

use statemanager in snap fetchers and fix the snapsync startup and snapsync test

functional snapsync integration with static peer state with hacks

rebase fixes

get static snapsync working again

typefix

fix spec

track safe and finalized in finalized

integrate building stat e with skeleton

integrate account fetcher with the skeleton

* move naive snapprogess tracker to synchronizer

* code cleanup and refactor

* update the fetching strategy and small progress flags refac

* add missing commit

* track cl syncsyncronization for snapsync start

* handle the sync failure and non completion scenarios

* add rudimentary snap progress to el status logs

* debug and fix the snapfetcher premature exits and add accountranges %age logging

* fix vmstep back

* track and log storage and byetcode progress

* pretty print stateroot

* Terminate storagefetcher after all storage and fragmented requests have been processed

* further fixes and improvements

* add early detection for snapsync state mismatch

* storage and codefetcher fixes

* lint

* fix statemanager test

* refactor finalized and safe block checks for availability and canonicality

* fix skeleton spec

* fix snap fetcher spec tests

* simplify fetcher's fetchPromise assignment

* fix cli spec

* fix fullsync spec

* improve the snapsync fetch flow

* small fix

* fix refac slip

* further code improvs

* fix valid log info

* cleanup

* increase coevragee

* sim cleanup

* add spec for formatBigDecimal

* keep unfinalized non canonical blocks around to handle some reorgs without backfill

* store annoucements in unfinalized

* improvements for the backfill from skeleton unfinalized blocks

* handle simple head reorg

---------

Co-authored-by: Amir <indigophi@protonmail.com>
Co-authored-by: Jochem Brouwer <jochembrouwer96@gmail.com>
Co-authored-by: Holger Drewes <Holger.Drewes@gmail.com>
  • Loading branch information
4 people authored Oct 31, 2023
1 parent ac9830f commit 9f91d22
Show file tree
Hide file tree
Showing 35 changed files with 1,663 additions and 528 deletions.
2 changes: 1 addition & 1 deletion packages/block/src/from-beacon-payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function executionPayloadFromBeaconPayload(payload: BeaconPayloadJson): E
gasLimit: bigIntToHex(BigInt(payload.gas_limit)),
gasUsed: bigIntToHex(BigInt(payload.gas_used)),
timestamp: bigIntToHex(BigInt(payload.timestamp)),
extraData: bigIntToHex(BigInt(payload.extra_data)),
extraData: payload.extra_data,
baseFeePerGas: bigIntToHex(BigInt(payload.base_fee_per_gas)),
blockHash: payload.block_hash,
transactions: payload.transactions,
Expand Down
12 changes: 3 additions & 9 deletions packages/client/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,13 +317,8 @@ const args: ClientOpts = yargs(hideBin(process.argv))
boolean: true,
default: true,
})
.option('disableBeaconSync', {
describe:
'Disables beacon (optimistic) sync if the CL provides blocks at the head of the chain',
boolean: true,
})
.option('forceSnapSync', {
describe: 'Force a snap sync run (for testing and development purposes)',
.option('snap', {
describe: 'Enable snap state sync (for testing and development purposes)',
boolean: true,
})
.option('prefixStorageTrieKeys', {
Expand Down Expand Up @@ -881,9 +876,8 @@ async function run() {
port: args.port,
saveReceipts: args.saveReceipts,
syncmode: args.sync,
disableBeaconSync: args.disableBeaconSync,
forceSnapSync: args.forceSnapSync,
prefixStorageTrieKeys: args.prefixStorageTrieKeys,
enableSnapSync: args.snap,
useStringValueTrieDB: args.useStringValueTrieDB,
txLookupLimit: args.txLookupLimit,
pruneEngineCache: args.pruneEngineCache,
Expand Down
59 changes: 30 additions & 29 deletions packages/client/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,11 @@ export interface ConfigOptions {
syncmode?: SyncMode

/**
* Whether to disable beacon (optimistic) sync if CL provides
* blocks at the head of chain.
* Whether to enable and run snapSync, currently experimental
*
* Default: false
*/
disableBeaconSync?: boolean

/**
* Whether to test and run snapSync. When fully ready, this needs to
* be replaced by a more sophisticated condition based on how far back we are
* from the head, and how to run it in conjunction with the beacon sync
* blocks at the head of chain.
*
* Default: false
*/
forceSnapSync?: boolean
enableSnapSync?: boolean

/**
* A temporary option to offer backward compatibility with already-synced databases that are
Expand Down Expand Up @@ -335,6 +324,8 @@ export interface ConfigOptions {
*/
maxInvalidBlocksErrorCache?: number
pruneEngineCache?: boolean
snapAvailabilityDepth?: bigint
snapTransitionSafeDepth?: bigint
}

export class Config {
Expand Down Expand Up @@ -368,7 +359,7 @@ export class Config {

public static readonly MAX_RANGE_BYTES = 50000
// This should get like 100 accounts in this range
public static readonly MAX_ACCOUNT_RANGE = (BIGINT_2 ** BIGINT_256 - BIGINT_1) / BigInt(1_000_000)
public static readonly MAX_ACCOUNT_RANGE = (BIGINT_2 ** BIGINT_256 - BIGINT_1) / BigInt(1_000)
// Larger ranges used for storage slots since assumption is slots should be much sparser than accounts
public static readonly MAX_STORAGE_RANGE = (BIGINT_2 ** BIGINT_256 - BIGINT_1) / BigInt(10)

Expand All @@ -381,6 +372,10 @@ export class Config {
public static readonly ENGINE_NEWPAYLOAD_MAX_EXECUTE = 2
// currently ethereumjs can execute 200 txs in 12 second window so keeping 1/2 target for blocking response
public static readonly ENGINE_NEWPAYLOAD_MAX_TXS_EXECUTE = 100
public static readonly SNAP_AVAILABILITY_DEPTH = BigInt(128)
// 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 readonly logger: Logger
public readonly syncmode: SyncMode
Expand Down Expand Up @@ -427,15 +422,16 @@ export class Config {
public readonly engineParentLookupMaxDepth: number
public readonly engineNewpayloadMaxExecute: number
public readonly engineNewpayloadMaxTxsExecute: number
public readonly snapAvailabilityDepth: bigint
public readonly snapTransitionSafeDepth: bigint

public readonly disableBeaconSync: boolean
public readonly forceSnapSync: boolean
// Just a development only flag, will/should be removed
public readonly disableSnapSync: boolean = false
public readonly prefixStorageTrieKeys: boolean
// Defaulting to false as experimental as of now
public readonly enableSnapSync: boolean
public readonly useStringValueTrieDB: boolean

public synchronized: boolean
public lastsyncronized?: boolean
/** lastSyncDate in ms */
public lastSyncDate: number
/** Best known block height */
Expand Down Expand Up @@ -464,7 +460,7 @@ export class Config {
this.txLookupLimit = options.txLookupLimit ?? 2350000
this.maxPerRequest = options.maxPerRequest ?? Config.MAXPERREQUEST_DEFAULT
this.maxFetcherJobs = options.maxFetcherJobs ?? Config.MAXFETCHERJOBS_DEFAULT
this.maxFetcherRequests = options.maxPerRequest ?? Config.MAXFETCHERREQUESTS_DEFAULT
this.maxFetcherRequests = options.maxFetcherRequests ?? Config.MAXFETCHERREQUESTS_DEFAULT
this.minPeers = options.minPeers ?? Config.MINPEERS_DEFAULT
this.maxPeers = options.maxPeers ?? Config.MAXPEERS_DEFAULT
this.dnsAddr = options.dnsAddr ?? Config.DNSADDR_DEFAULT
Expand Down Expand Up @@ -510,10 +506,12 @@ export class Config {
options.engineNewpayloadMaxExecute ?? Config.ENGINE_NEWPAYLOAD_MAX_EXECUTE
this.engineNewpayloadMaxTxsExecute =
options.engineNewpayloadMaxTxsExecute ?? Config.ENGINE_NEWPAYLOAD_MAX_TXS_EXECUTE
this.snapAvailabilityDepth = options.snapAvailabilityDepth ?? Config.SNAP_AVAILABILITY_DEPTH
this.snapTransitionSafeDepth =
options.snapTransitionSafeDepth ?? Config.SNAP_TRANSITION_SAFE_DEPTH

this.disableBeaconSync = options.disableBeaconSync ?? false
this.forceSnapSync = options.forceSnapSync ?? false
this.prefixStorageTrieKeys = options.prefixStorageTrieKeys ?? true
this.enableSnapSync = options.enableSnapSync ?? false
this.useStringValueTrieDB = options.useStringValueTrieDB ?? false

// Start it off as synchronized if this is configured to mine or as single node
Expand Down Expand Up @@ -597,13 +595,16 @@ export class Config {
}
}

this.logger.debug(
`Client synchronized=${this.synchronized}${
latest !== null && latest !== undefined ? ' height=' + latest.number : ''
} syncTargetHeight=${this.syncTargetHeight} lastSyncDate=${
(Date.now() - this.lastSyncDate) / 1000
} secs ago`
)
if (this.synchronized !== this.lastsyncronized) {
this.logger.debug(
`Client synchronized=${this.synchronized}${
latest !== null && latest !== undefined ? ' height=' + latest.number : ''
} syncTargetHeight=${this.syncTargetHeight} lastSyncDate=${
(Date.now() - this.lastSyncDate) / 1000
} secs ago`
)
this.lastsyncronized = this.synchronized
}
}

/**
Expand Down Expand Up @@ -680,7 +681,7 @@ export class Config {
*/
getDnsDiscovery(option: boolean | undefined): boolean {
if (option !== undefined) return option
const dnsNets = ['holesky', 'sepolia']
const dnsNets = ['goerli', 'sepolia', 'holesky']
return dnsNets.includes(this.chainCommon.chainName())
}
}
83 changes: 41 additions & 42 deletions packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,41 +233,26 @@ export class VMExecution extends Execution {
async setHead(
blocks: Block[],
{ finalizedBlock, safeBlock }: { finalizedBlock?: Block; safeBlock?: Block } = {}
): Promise<void> {
return this.runWithLock<void>(async () => {
const vmHeadBlock = blocks[blocks.length - 1]
const chainPointers: [string, Block][] = [
['vmHeadBlock', vmHeadBlock],
// if safeBlock is not provided, the current safeBlock of chain should be used
// which is genesisBlock if it has never been set for e.g.
['safeBlock', safeBlock ?? this.chain.blocks.safe ?? this.chain.genesis],
['finalizedBlock', finalizedBlock ?? this.chain.blocks.finalized ?? this.chain.genesis],
]

let isSortedDesc = true
let lastBlock = vmHeadBlock
for (const [blockName, block] of chainPointers) {
if (block === null) {
continue
}
if (!(await this.vm.stateManager.hasStateRoot(block.header.stateRoot))) {
// If we set blockchain iterator to somewhere where we don't have stateroot
// execution run will always fail
throw Error(
`${blockName}'s stateRoot not found number=${block.header.number} root=${short(
block.header.stateRoot
)}`
)
}
isSortedDesc = isSortedDesc && lastBlock.header.number >= block.header.number
lastBlock = block
}
): Promise<boolean> {
if (!this.started || this.config.shutdown) return false

if (isSortedDesc === false) {
return this.runWithLock<boolean>(async () => {
const vmHeadBlock = blocks[blocks.length - 1]
const chainPointers: [string, Block][] = [['vmHeadBlock', vmHeadBlock]]

// instead of checking for the previous roots of safe,finalized, we will contend
// ourselves with just vmHead because in snap sync we might not have the safe
// finalized blocks executed
if (!(await this.vm.stateManager.hasStateRoot(vmHeadBlock.header.stateRoot))) {
// If we set blockchain iterator to somewhere where we don't have stateroot
// execution run will always fail
throw Error(
`headBlock=${chainPointers[0][1].header.number} should be >= safeBlock=${chainPointers[1][1]?.header.number} should be >= finalizedBlock=${chainPointers[2][1]?.header.number}`
`vmHeadBlock's stateRoot not found number=${vmHeadBlock.header.number} root=${short(
vmHeadBlock.header.stateRoot
)}`
)
}

// skip emitting the chain update event as we will manually do it
await this.chain.putBlocks(blocks, true, true)
for (const block of blocks) {
Expand Down Expand Up @@ -296,6 +281,7 @@ export class VMExecution extends Execution {
await this.chain.blockchain.setIteratorHead('finalized', finalizedBlock.hash())
}
await this.chain.update(true)
return true
})
}

Expand Down Expand Up @@ -361,7 +347,7 @@ export class VMExecution extends Execution {
// determine starting state for block run
// if we are just starting or if a chain reorg has happened
if (headBlock === undefined || reorg) {
const headBlock = await blockchain.getBlock(block.header.parentHash)
headBlock = await blockchain.getBlock(block.header.parentHash)
parentState = headBlock.header.stateRoot

if (reorg) {
Expand Down Expand Up @@ -508,21 +494,34 @@ export class VMExecution extends Execution {
// to parent's parent and so on...
//
// There can also be a better way to backstep vm to but lets naively step back
let backStepTo, backStepToHash
let backStepTo,
backStepToHash,
backStepToRoot,
hasParentStateRoot = false
if (headBlock !== undefined) {
hasParentStateRoot = await this.vm.stateManager.hasStateRoot(
headBlock.header.stateRoot
)
backStepTo = headBlock.header.number ?? BIGINT_0 - BIGINT_1
backStepToHash = headBlock.header.parentHash
backStepToRoot = headBlock.header.stateRoot
}
this.config.logger.warn(
`${errorMsg}, backStepping vmHead to number=${backStepTo} hash=${short(
backStepToHash ?? 'na'
)}:\n${error}`
)

// backStepToHash should not be undefined but if its the above warn log will show us to debug
// but still handle here so that we don't send the client into a tizzy
if (backStepToHash !== undefined) {

if (hasParentStateRoot === true && backStepToHash !== undefined) {
this.config.logger.warn(
`${errorMsg}, backStepping vmHead to number=${backStepTo} hash=${short(
backStepToHash ?? 'na'
)} hasParentStateRoot=${short(backStepToRoot ?? 'na')}:\n${error}`
)
await this.vm.blockchain.setIteratorHead('vm', backStepToHash)
} else {
this.config.logger.error(
`${errorMsg}, couldn't back step to vmHead number=${backStepTo} hash=${short(
backStepToHash ?? 'na'
)} hasParentStateRoot=${hasParentStateRoot} backStepToRoot=${short(
backStepToRoot ?? 'na'
)}:\n${error}`
)
}
} else {
this.config.logger.warn(`${errorMsg}:\n${error}`)
Expand Down
Loading

0 comments on commit 9f91d22

Please sign in to comment.