From 104e6e88cdd87bf4c7bbaa8f6ee0955be5fc2649 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 10 Oct 2023 12:51:22 +0200 Subject: [PATCH 01/14] client/engine: newPayload match spec invalid blockhash [no ci] --- packages/client/src/rpc/modules/engine.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/client/src/rpc/modules/engine.ts b/packages/client/src/rpc/modules/engine.ts index e5ea56bf9f..5ba4cc9a6e 100644 --- a/packages/client/src/rpc/modules/engine.ts +++ b/packages/client/src/rpc/modules/engine.ts @@ -993,6 +993,7 @@ export class Engine { const newPayloadRes = await this.newPayload(params) if (newPayloadRes.status === Status.INVALID_BLOCK_HASH) { newPayloadRes.status = Status.INVALID + newPayloadRes.latestValidHash = null } return newPayloadRes } @@ -1010,6 +1011,7 @@ export class Engine { const newPayloadRes = await this.newPayload(params) if (newPayloadRes.status === Status.INVALID_BLOCK_HASH) { newPayloadRes.status = Status.INVALID + newPayloadRes.latestValidHash = null } return newPayloadRes } From b525804a5e93f64429a1b7ab0e85118defd31c4d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 10 Oct 2023 15:10:02 +0200 Subject: [PATCH 02/14] client/engine: add check for genesis block in recursivelyFindParents [no ci] --- packages/client/src/rpc/modules/engine.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/client/src/rpc/modules/engine.ts b/packages/client/src/rpc/modules/engine.ts index 5ba4cc9a6e..ceb817dbf9 100644 --- a/packages/client/src/rpc/modules/engine.ts +++ b/packages/client/src/rpc/modules/engine.ts @@ -276,6 +276,11 @@ const recursivelyFindParents = async ( ) parentBlocks.push(block) + if (block.isGenesis()) { + // In case we hit the genesis block we should stop finding additional parents + break + } + // throw error if lookups have exceeded maxDepth if (parentBlocks.length > maxDepth) { throw Error(`recursivelyFindParents lookups deeper than maxDepth=${maxDepth}`) From 9660573689f1054952f251c34371447a014e09f9 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Oct 2023 00:14:45 +0200 Subject: [PATCH 03/14] blockchain/client: ensure safe/finalized blocks have to be set [no ci] --- packages/blockchain/src/blockchain.ts | 26 +++++++++++++++--- packages/client/src/blockchain/chain.ts | 32 ++++++++++++++--------- packages/client/src/rpc/error-code.ts | 3 +++ packages/client/src/rpc/helpers.ts | 21 ++++++++++++--- packages/client/src/rpc/modules/engine.ts | 2 +- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 7fa482d3ce..e022a52410 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -312,13 +312,31 @@ export class Blockchain implements BlockchainInterface { */ async getIteratorHead(name = 'vm'): Promise { return this.runWithLock(async () => { - // if the head is not found return the genesis hash - const hash = this._heads[name] ?? this.genesisBlock.hash() - const block = await this.getBlock(hash) - return block + return (await this.getHead(name, false))! }) } + /** + * This method differs from `getIteratorHead`. If the head is not found, it returns `undefined`. + * @param name - Optional name of the iterator head (default: 'vm') + * @returns + */ + async getIteratorHeadSafe(name = 'vm'): Promise { + return this.runWithLock(async () => { + return this.getHead(name, true) + }) + } + + private async getHead(name: string, returnUndefinedIfNotSet: boolean = false) { + const headHash = this._heads[name] + if (headHash === undefined && returnUndefinedIfNotSet) { + return undefined + } + const hash = this._heads[name] ?? this.genesisBlock.hash() + const block = await this.getBlock(hash) + return block + } + /** * Returns the latest header in the canonical chain. */ diff --git a/packages/client/src/blockchain/chain.ts b/packages/client/src/blockchain/chain.ts index 61bf4cb795..78c0ce06e3 100644 --- a/packages/client/src/blockchain/chain.ts +++ b/packages/client/src/blockchain/chain.ts @@ -294,18 +294,26 @@ export class Chain { height: BIGINT_0, } - headers.latest = await this.getCanonicalHeadHeader() + blocks.latest = await this.getCanonicalHeadBlock() + blocks.finalized = (await this.getCanonicalFinalizedBlock()) ?? null + blocks.safe = (await this.getCanonicalSafeBlock()) ?? null + blocks.vm = await this.getCanonicalVmHead() + + headers.latest = blocks.latest.header // finalized and safe are always blocks since they have to have valid execution // before they can be saved in chain - headers.finalized = (await this.getCanonicalFinalizedBlock()).header - headers.safe = (await this.getCanonicalSafeBlock()).header + if (blocks.finalized !== null) { + headers.finalized = blocks.finalized.header + } else { + headers.finalized = null + } + if (blocks.safe !== null) { + headers.safe = blocks.safe.header + } else { + headers.safe = null + } headers.vm = (await this.getCanonicalVmHead()).header - blocks.latest = await this.getCanonicalHeadBlock() - blocks.finalized = await this.getCanonicalFinalizedBlock() - blocks.safe = await this.getCanonicalSafeBlock() - blocks.vm = await this.getCanonicalVmHead() - headers.height = headers.latest.number blocks.height = blocks.latest.header.number @@ -513,17 +521,17 @@ export class Chain { /** * Gets the latest block in the canonical chain */ - async getCanonicalSafeBlock(): Promise { + async getCanonicalSafeBlock(): Promise { if (!this.opened) throw new Error('Chain closed') - return this.blockchain.getIteratorHead('safe') + return this.blockchain.getIteratorHeadSafe('safe') } /** * Gets the latest block in the canonical chain */ - async getCanonicalFinalizedBlock(): Promise { + async getCanonicalFinalizedBlock(): Promise { if (!this.opened) throw new Error('Chain closed') - return this.blockchain.getIteratorHead('finalized') + return this.blockchain.getIteratorHeadSafe('finalized') } /** diff --git a/packages/client/src/rpc/error-code.ts b/packages/client/src/rpc/error-code.ts index 422c6c1b21..b9f3327288 100644 --- a/packages/client/src/rpc/error-code.ts +++ b/packages/client/src/rpc/error-code.ts @@ -19,3 +19,6 @@ export const validEngineCodes = [ UNSUPPORTED_FORK, UNKNOWN_PAYLOAD, ] + +// Errors for the ETH protocol +export const INVALID_BLOCK = -39001 diff --git a/packages/client/src/rpc/helpers.ts b/packages/client/src/rpc/helpers.ts index 66d4602bba..bda82aeee2 100644 --- a/packages/client/src/rpc/helpers.ts +++ b/packages/client/src/rpc/helpers.ts @@ -1,6 +1,6 @@ import { BIGINT_0, bigIntToHex, bytesToHex, intToHex } from '@ethereumjs/util' -import { INTERNAL_ERROR, INVALID_PARAMS } from './error-code' +import { INTERNAL_ERROR, INVALID_BLOCK, INVALID_PARAMS } from './error-code' import type { Chain } from '../blockchain' import type { Block } from '@ethereumjs/block' @@ -73,6 +73,7 @@ export const getBlockByOption = async (blockOpt: string, chain: Chain) => { } let block: Block + let tempBlock: Block | undefined // Used in `safe` and `finalized` blocks const latest = chain.blocks.latest ?? (await chain.getCanonicalHeadBlock()) switch (blockOpt) { @@ -83,10 +84,24 @@ export const getBlockByOption = async (blockOpt: string, chain: Chain) => { block = latest break case 'safe': - block = chain.blocks.safe ?? (await chain.getCanonicalSafeBlock()) + tempBlock = chain.blocks.safe ?? (await chain.getCanonicalSafeBlock()) + if (tempBlock === null || tempBlock === undefined) { + throw { + message: 'Unknown block', + code: INVALID_BLOCK, + } + } + block = tempBlock break case 'finalized': - block = chain.blocks.finalized ?? (await chain.getCanonicalFinalizedBlock()) + tempBlock = chain.blocks.finalized ?? (await chain.getCanonicalFinalizedBlock()) + if (tempBlock === null || tempBlock === undefined) { + throw { + message: 'Unknown block', + code: INVALID_BLOCK, + } + } + block = tempBlock break default: { const blockNumber = BigInt(blockOpt) diff --git a/packages/client/src/rpc/modules/engine.ts b/packages/client/src/rpc/modules/engine.ts index 14473e7c7d..9db37bbdac 100644 --- a/packages/client/src/rpc/modules/engine.ts +++ b/packages/client/src/rpc/modules/engine.ts @@ -1238,7 +1238,7 @@ export class Engine { if (!isPrevSynced && this.chain.config.synchronized) { this.service.txPool.checkRunState() } - } else { + } else if (!headBlock.isGenesis()) { // even if the vmHead is same still validations need to be done regarding the correctness // of the sequence and canonical-ity try { From ce1f9d0c5d83c431d72d97c68955f4296299fb17 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Oct 2023 10:05:34 +0200 Subject: [PATCH 04/14] blockchain: accept putting the same genesis block as in blockchain genesis [no ci] --- packages/blockchain/src/blockchain.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index e022a52410..765c47905a 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -468,7 +468,13 @@ export class Blockchain implements BlockchainInterface { // we cannot overwrite the Genesis block after initializing the Blockchain if (isGenesis) { - throw new Error('Cannot put a genesis block: create a new Blockchain') + if (equalsBytes(this.genesisBlock.hash(), block.hash())) { + // Try to re-put the exisiting genesis block, accept this + return + } + throw new Error( + 'Cannot put a different genesis block than current blockchain genesis: create a new Blockchain' + ) } const { header } = block From 77f9f53a2758afb7b209cb6b78cda941b405ccd7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Oct 2023 17:57:11 +0200 Subject: [PATCH 05/14] client: default to genesis if safe/finalized is not set in chain [no ci] --- packages/client/src/execution/vmexecution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 1545a36b28..1deb9a371e 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -229,12 +229,12 @@ export class VMExecution extends Execution { ): Promise { return this.runWithLock(async () => { const vmHeadBlock = blocks[blocks.length - 1] - const chainPointers: [string, Block | null][] = [ + 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], - ['finalizedBlock', finalizedBlock ?? this.chain.blocks.finalized], + ['safeBlock', safeBlock ?? this.chain.blocks.safe ?? this.chain.genesis], + ['finalizedBlock', finalizedBlock ?? this.chain.blocks.finalized ?? this.chain.genesis], ] let isSortedDesc = true From a6168554f615bbebb1834edafc5d536c2d09be58 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 12 Oct 2023 17:57:27 +0200 Subject: [PATCH 06/14] client: fix log on chain pointers [no ci] --- packages/client/src/execution/vmexecution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/execution/vmexecution.ts b/packages/client/src/execution/vmexecution.ts index 1deb9a371e..1b56cb74b6 100644 --- a/packages/client/src/execution/vmexecution.ts +++ b/packages/client/src/execution/vmexecution.ts @@ -258,7 +258,7 @@ export class VMExecution extends Execution { if (isSortedDesc === false) { throw Error( - `headBlock=${vmHeadBlock?.header.number} should be >= safeBlock=${safeBlock?.header.number} should be >= finalizedBlock=${finalizedBlock?.header.number}` + `headBlock=${chainPointers[0][1].header.number} should be >= safeBlock=${chainPointers[1][1]?.header.number} should be >= finalizedBlock=${chainPointers[2][1]?.header.number}` ) } // skip emitting the chain update event as we will manually do it From f2aedcac4ca5cda066733b884a58d0e81ded17ed Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 16 Oct 2023 12:14:02 +0200 Subject: [PATCH 07/14] common: fix off-by-one check [no ci] --- packages/common/src/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 8b4aa5efff..f4394c1519 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -384,7 +384,7 @@ export class Common { if (mergeIndex >= 0 && td !== undefined && td !== null) { if (hfIndex >= mergeIndex && BigInt(hfs[mergeIndex].ttd!) > td) { throw Error('Maximum HF determined by total difficulty is lower than the block number HF') - } else if (hfIndex < mergeIndex && BigInt(hfs[mergeIndex].ttd!) <= td) { + } else if (hfIndex < mergeIndex && BigInt(hfs[mergeIndex].ttd!) < td) { throw Error('HF determined by block number is lower than the minimum total difficulty HF') } } From 14b60a4943e56d09bf4e9975305c4674dc02c6fd Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 16 Oct 2023 13:38:57 +0200 Subject: [PATCH 08/14] client/miner: ensure different payloadID for different withdrawals [no ci] --- packages/client/src/miner/pendingBlock.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/client/src/miner/pendingBlock.ts b/packages/client/src/miner/pendingBlock.ts index 7638e3457f..1afcbeb569 100644 --- a/packages/client/src/miner/pendingBlock.ts +++ b/packages/client/src/miner/pendingBlock.ts @@ -1,6 +1,7 @@ import { Hardfork } from '@ethereumjs/common' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { + Address, BIGINT_1, BIGINT_2, TypeOutput, @@ -130,6 +131,22 @@ export class PendingBlock { toType(parentBeaconBlockRoot!, TypeOutput.Uint8Array) ?? zeros(32) const coinbaseBuf = toType(coinbase ?? zeros(20), TypeOutput.Uint8Array) + let withdrawalsBuf = zeros(0) + + if (withdrawals !== undefined) { + const withdrawalsBufTemp: Uint8Array[] = [] + for (const withdrawal of withdrawals) { + const indexBuf = bigIntToUnpaddedBytes(toType(withdrawal.index ?? 0, TypeOutput.BigInt)) + const validatorIndex = bigIntToUnpaddedBytes( + toType(withdrawal.validatorIndex ?? 0, TypeOutput.BigInt) + ) + const address = toType(withdrawal.address ?? Address.zero(), TypeOutput.Uint8Array) + const amount = bigIntToUnpaddedBytes(toType(withdrawal.amount ?? 0, TypeOutput.BigInt)) + withdrawalsBufTemp.push(concatBytes(indexBuf, validatorIndex, address, amount)) + } + withdrawalsBuf = concatBytes(...withdrawalsBufTemp) + } + const payloadIdBytes = toBytes( keccak256( concatBytes( @@ -138,7 +155,8 @@ export class PendingBlock { timestampBuf, gasLimitBuf, parentBeaconBlockRootBuf, - coinbaseBuf + coinbaseBuf, + withdrawalsBuf ) ).subarray(0, 8) ) From 3c2f82071c77abd9505da13709f53b54596ba737 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 16 Oct 2023 13:55:26 +0200 Subject: [PATCH 09/14] blockchain: fix test --- packages/blockchain/test/index.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/blockchain/test/index.spec.ts b/packages/blockchain/test/index.spec.ts index 3190a78d04..0badf1f26c 100644 --- a/packages/blockchain/test/index.spec.ts +++ b/packages/blockchain/test/index.spec.ts @@ -844,7 +844,7 @@ describe('initialization tests', () => { } catch (e: any) { assert.equal( e.message, - 'Cannot put a genesis block: create a new Blockchain', + 'Cannot put a different genesis block than current blockchain genesis: create a new Blockchain', 'putting a genesis block did throw (otherGenesisBlock not found in chain)' ) } From fd3ce1e65e3dc084e09e9da30cc13f5b1c81be72 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 17 Oct 2023 08:44:17 -0400 Subject: [PATCH 10/14] Fix common test --- packages/common/test/mergePOS.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/test/mergePOS.spec.ts b/packages/common/test/mergePOS.spec.ts index a9cb3df041..7a7e73ad70 100644 --- a/packages/common/test/mergePOS.spec.ts +++ b/packages/common/test/mergePOS.spec.ts @@ -128,7 +128,7 @@ describe('[Common]: Merge/POS specific logic', () => { assert.ok(e.message.includes(eMsg), msg) } try { - c.setHardforkBy({ blockNumber: 14n, td: 5000n }) + c.setHardforkBy({ blockNumber: 14n, td: 5001n }) assert.fail(`should have thrown td > ttd validation error`) } catch (e: any) { msg = 'block number < last HF block number set, TD set and higher (should throw)' From 80c262471a7ca0c597c748d1294a2954bebc5288 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 20 Oct 2023 16:35:47 +0200 Subject: [PATCH 11/14] client: update forkchoice test --- packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts index c1b717f9a3..c9c7ded54d 100644 --- a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts +++ b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts @@ -280,7 +280,7 @@ describe(method, () => { }) it('latest block after reorg', async () => { - const { server } = await setupChain(genesisJSON, 'post-merge', { engine: true }) + const { server, blockchain } = await setupChain(genesisJSON, 'post-merge', { engine: true }) let req = params(method, [validForkChoiceState]) let expectRes = (res: any) => { assert.equal(res.body.result.payloadStatus.status, 'VALID') @@ -294,6 +294,7 @@ describe(method, () => { ...validForkChoiceState, headBlockHash: blocks[2].blockHash, safeBlockHash: blocks[0].blockHash, + finalizedBlockHash: bytesToHex(blockchain.genesisBlock.hash()), }, ]) expectRes = (res: any) => { From da48eca495fc2f82856df6293225b6185e2e8bb4 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Oct 2023 17:59:51 +0200 Subject: [PATCH 12/14] client: address review --- packages/client/src/blockchain/chain.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/client/src/blockchain/chain.ts b/packages/client/src/blockchain/chain.ts index 78c0ce06e3..f510c65b70 100644 --- a/packages/client/src/blockchain/chain.ts +++ b/packages/client/src/blockchain/chain.ts @@ -302,16 +302,8 @@ export class Chain { headers.latest = blocks.latest.header // finalized and safe are always blocks since they have to have valid execution // before they can be saved in chain - if (blocks.finalized !== null) { - headers.finalized = blocks.finalized.header - } else { - headers.finalized = null - } - if (blocks.safe !== null) { - headers.safe = blocks.safe.header - } else { - headers.safe = null - } + headers.finalized = blocks.finalized?.header ?? null + headers.safe = blocks.safe?.header ?? null headers.vm = (await this.getCanonicalVmHead()).header headers.height = headers.latest.number From 586868ba51d9d3beddd5961fa44d667c4d547d8d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Sun, 22 Oct 2023 18:11:05 +0200 Subject: [PATCH 13/14] client: ensure chain head updates correctly --- packages/client/src/blockchain/chain.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/src/blockchain/chain.ts b/packages/client/src/blockchain/chain.ts index f510c65b70..e3c8c8097e 100644 --- a/packages/client/src/blockchain/chain.ts +++ b/packages/client/src/blockchain/chain.ts @@ -294,18 +294,18 @@ export class Chain { height: BIGINT_0, } - blocks.latest = await this.getCanonicalHeadBlock() - blocks.finalized = (await this.getCanonicalFinalizedBlock()) ?? null - blocks.safe = (await this.getCanonicalSafeBlock()) ?? null - blocks.vm = await this.getCanonicalVmHead() - - headers.latest = blocks.latest.header + headers.latest = await this.getCanonicalHeadHeader() // finalized and safe are always blocks since they have to have valid execution // before they can be saved in chain headers.finalized = blocks.finalized?.header ?? null headers.safe = blocks.safe?.header ?? null headers.vm = (await this.getCanonicalVmHead()).header + blocks.latest = await this.getCanonicalHeadBlock() + blocks.finalized = (await this.getCanonicalFinalizedBlock()) ?? null + blocks.safe = (await this.getCanonicalSafeBlock()) ?? null + blocks.vm = await this.getCanonicalVmHead() + headers.height = headers.latest.number blocks.height = blocks.latest.header.number From 53574edea366d16947da809dd311a238afc80421 Mon Sep 17 00:00:00 2001 From: Gabriel Rocheleau Date: Sun, 22 Oct 2023 22:30:30 -0400 Subject: [PATCH 14/14] client: reorder blocks, headers assignments and avoid duplicate VmHead call --- packages/client/src/blockchain/chain.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/client/src/blockchain/chain.ts b/packages/client/src/blockchain/chain.ts index e3c8c8097e..6c1aae8d66 100644 --- a/packages/client/src/blockchain/chain.ts +++ b/packages/client/src/blockchain/chain.ts @@ -294,17 +294,17 @@ export class Chain { height: BIGINT_0, } + blocks.latest = await this.getCanonicalHeadBlock() + blocks.finalized = (await this.getCanonicalFinalizedBlock()) ?? null + blocks.safe = (await this.getCanonicalSafeBlock()) ?? null + blocks.vm = await this.getCanonicalVmHead() + headers.latest = await this.getCanonicalHeadHeader() // finalized and safe are always blocks since they have to have valid execution // before they can be saved in chain headers.finalized = blocks.finalized?.header ?? null headers.safe = blocks.safe?.header ?? null - headers.vm = (await this.getCanonicalVmHead()).header - - blocks.latest = await this.getCanonicalHeadBlock() - blocks.finalized = (await this.getCanonicalFinalizedBlock()) ?? null - blocks.safe = (await this.getCanonicalSafeBlock()) ?? null - blocks.vm = await this.getCanonicalVmHead() + headers.vm = blocks.vm.header headers.height = headers.latest.number blocks.height = blocks.latest.header.number