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

client: integrate snapsync on experimental basis #3031

Merged
merged 39 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
1c93efb
client: integrate snapsync on experimental basis
g11tech Sep 18, 2023
badf674
move naive snapprogess tracker to synchronizer
g11tech Oct 11, 2023
51d7852
code cleanup and refactor
g11tech Oct 11, 2023
dd83ecb
update the fetching strategy and small progress flags refac
g11tech Oct 12, 2023
951a66e
add missing commit
g11tech Oct 12, 2023
e12175a
track cl syncsyncronization for snapsync start
g11tech Oct 12, 2023
b69efa9
handle the sync failure and non completion scenarios
g11tech Oct 12, 2023
ced2b46
add rudimentary snap progress to el status logs
g11tech Oct 12, 2023
c89d9d2
debug and fix the snapfetcher premature exits and add accountranges %…
g11tech Oct 13, 2023
773b197
fix vmstep back
g11tech Oct 13, 2023
b582cc9
track and log storage and byetcode progress
g11tech Oct 13, 2023
943985b
pretty print stateroot
g11tech Oct 13, 2023
6c4cda9
Terminate storagefetcher after all storage and fragmented requests ha…
scorbajio Oct 16, 2023
cdeb6b0
further fixes and improvements
g11tech Oct 20, 2023
f9edf49
add early detection for snapsync state mismatch
g11tech Oct 20, 2023
5947508
storage and codefetcher fixes
g11tech Oct 23, 2023
e596dd3
lint
g11tech Oct 23, 2023
0447f14
fix statemanager test
g11tech Oct 24, 2023
30b85ce
refactor finalized and safe block checks for availability and canonic…
g11tech Oct 25, 2023
1c69a9a
fix skeleton spec
g11tech Oct 25, 2023
f0faee3
fix snap fetcher spec tests
g11tech Oct 26, 2023
a0f39ea
simplify fetcher's fetchPromise assignment
g11tech Oct 26, 2023
bdc56d4
fix cli spec
g11tech Oct 26, 2023
7607e48
fix fullsync spec
g11tech Oct 26, 2023
6d9e547
improve the snapsync fetch flow
g11tech Oct 26, 2023
e651bc7
small fix
g11tech Oct 26, 2023
45e1d8a
fix refac slip
g11tech Oct 26, 2023
688e453
further code improvs
g11tech Oct 27, 2023
d676230
fix valid log info
g11tech Oct 27, 2023
e2eefdd
cleanup
g11tech Oct 27, 2023
67ee6ac
increase coevragee
g11tech Oct 27, 2023
a413212
sim cleanup
g11tech Oct 27, 2023
1c791b4
add spec for formatBigDecimal
g11tech Oct 27, 2023
b6cc74b
Merge branch 'master' into integrate-snapsync
jochem-brouwer Oct 27, 2023
b4de435
keep unfinalized non canonical blocks around to handle some reorgs wi…
g11tech Oct 28, 2023
9454a97
store annoucements in unfinalized
g11tech Oct 28, 2023
9c0506f
improvements for the backfill from skeleton unfinalized blocks
g11tech Oct 28, 2023
b09794f
handle simple head reorg
g11tech Oct 29, 2023
ea4076d
Merge branch 'master' into integrate-snapsync
holgerd77 Oct 31, 2023
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
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']
Copy link
Member

Choose a reason for hiding this comment

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

Ah, HERE this was hidden!

(I wondered why this never made it to master weeks after we had discussed 😂)

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 @@
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

Check warning on line 248 in packages/client/src/execution/vmexecution.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/execution/vmexecution.ts#L247-L248

Added lines #L247 - L248 were not covered by tests
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 @@
await this.chain.blockchain.setIteratorHead('finalized', finalizedBlock.hash())
}
await this.chain.update(true)
return true
})
}

Expand Down Expand Up @@ -361,7 +347,7 @@
// 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 @@
// 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
)

Check warning on line 504 in packages/client/src/execution/vmexecution.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/execution/vmexecution.ts#L502-L504

Added lines #L502 - L504 were not covered by tests
backStepTo = headBlock.header.number ?? BIGINT_0 - BIGINT_1
backStepToHash = headBlock.header.parentHash
backStepToRoot = headBlock.header.stateRoot

Check warning on line 507 in packages/client/src/execution/vmexecution.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/execution/vmexecution.ts#L507

Added line #L507 was not covered by tests
}
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'

Check warning on line 513 in packages/client/src/execution/vmexecution.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/execution/vmexecution.ts#L509-L513

Added lines #L509 - L513 were not covered by tests
)} hasParentStateRoot=${short(backStepToRoot ?? 'na')}:\n${error}`
)
await this.vm.blockchain.setIteratorHead('vm', backStepToHash)
} else {
this.config.logger.error(

Check warning on line 518 in packages/client/src/execution/vmexecution.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/execution/vmexecution.ts#L517-L518

Added lines #L517 - L518 were not covered by tests
`${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
Loading