Skip to content

Commit

Permalink
Implement debug_setHead (#3811)
Browse files Browse the repository at this point in the history
* Implement debug_setHead

* Update packages/client/src/rpc/modules/debug.ts

Co-authored-by: acolytec3 <[email protected]>

* Complete the implementation of debug_setHead

* Add helpers for creating blockchains and blocks for testing

* Add test for debug_setHead

* Set head of vmexecution as well

* Do not prerun block before setting as vmexecution head

* Move and export testSetup function and clean up tests

* Remove unused util functions

* Do not return nonstandard string from debug_setHead

* Fix lint issues

---------

Co-authored-by: acolytec3 <[email protected]>
  • Loading branch information
scorbajio and acolytec3 authored Dec 16, 2024
1 parent 662af35 commit 4da1f03
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 10 deletions.
29 changes: 29 additions & 0 deletions packages/client/src/rpc/modules/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ export class Debug {
1,
[[validators.hex]],
)
this.setHead = middleware(callWithStackTrace(this.setHead.bind(this), this._rpcDebug), 1, [
[validators.blockOption],
])
this.verbosity = middleware(callWithStackTrace(this.verbosity.bind(this), this._rpcDebug), 1, [
[validators.unsignedInteger],
])
Expand Down Expand Up @@ -457,4 +460,30 @@ export class Debug {
this.client.config.logger.configure({ level: logLevels[level] })
return `level: ${this.client.config.logger.level}`
}

/**
* Sets the current head of the local chain by block number. Note, this is a
* destructive action and may severely damage your chain. Use with extreme
* caution.
* @param blockOpt Block number or tag to set as head of chain
*/
async setHead(params: [string]) {
const [blockOpt] = params
if (blockOpt === 'pending') {
throw {
code: INVALID_PARAMS,
message: `"pending" is not supported`,
}
}

const block = await getBlockByOption(blockOpt, this.chain)
try {
await this.service.skeleton?.setHead(block, true)
await this.service.execution.setHead([block])
} catch (e) {
throw {
code: INTERNAL_ERROR,
}
}
}
}
11 changes: 1 addition & 10 deletions packages/client/test/execution/vmexecution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { assert, describe, it } from 'vitest'
import { Chain } from '../../src/blockchain/index.js'
import { Config } from '../../src/config.js'
import { VMExecution } from '../../src/execution/index.js'
import { closeRPC, setupChain } from '../rpc/helpers.js'
import { closeRPC, setupChain, testSetup } from '../rpc/helpers.js'
import { goerliData } from '../testdata/blocks/goerli.js'
import { mainnetData } from '../testdata/blocks/mainnet.js'
import { testnetData } from '../testdata/common/testnet.js'
Expand Down Expand Up @@ -94,15 +94,6 @@ describe('[VMExecution]', () => {
assert.equal(exec.vm, vm, 'should use vm provided')
})

async function testSetup(blockchain: Blockchain, common?: Common) {
const config = new Config({ common, accountCache: 10000, storageCache: 1000 })
const chain = await Chain.create({ config, blockchain })
const exec = new VMExecution({ config, chain })
await chain.open()
await exec.open()
return exec
}

it('Block execution / Hardforks PoW (mainnet)', async () => {
let blockchain = await createBlockchain({
validateBlocks: true,
Expand Down
48 changes: 48 additions & 0 deletions packages/client/test/rpc/debug/setHead.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { createBlockchainFromBlocksData } from '@ethereumjs/blockchain'
import { assert, describe, it } from 'vitest'

import { mainnetData } from '../../testdata/blocks/mainnet.js'
import { createClient, createManager, getRPCClient, startRPC, testSetup } from '../helpers.js'

import type { Blockchain } from '@ethereumjs/blockchain'

const method = 'debug_setHead'

describe(method, async () => {
it('call with valid arguments', async () => {
const blockchain = await createBlockchainFromBlocksData(mainnetData, {
validateBlocks: true,
validateConsensus: false,
})
const blocks = await blockchain.getBlocks(0, 6, 0, false)
const exec = await testSetup(blockchain)
await exec.run()
const newHead = await (exec.vm.blockchain as Blockchain).getIteratorHead!()
assert.equal(newHead.header.number, BigInt(5), 'should run all blocks')

const a = await createClient({ blockchain })
await a.service.skeleton?.open()
;(a.service.execution as any) = exec

const manager = createManager(a)
const rpc = getRPCClient(startRPC(manager.getMethods()))
assert.equal(
await a.service.skeleton?.headHash(),
undefined,
'should return undefined when head is not set',
)
for (let i = 0; i < blocks.length; i++) {
await rpc.request(method, [`0x${i}`])
assert.deepEqual(
await a.service.skeleton?.headHash()!,
blocks[i].header.hash(),
`skeleton chain should return hash of block number ${i} set as head`,
)
assert.deepEqual(
a.service.execution.chainStatus?.hash!,
blocks[i].header.hash(),
`vm execution should set hash to new head`,
)
}
}, 30000)
})
9 changes: 9 additions & 0 deletions packages/client/test/rpc/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,3 +348,12 @@ export const batchBlocks = async (rpc: HttpClient, inputBlocks: any[]) => {
assert.equal(res.result.status, 'VALID')
}
}

export async function testSetup(blockchain: Blockchain, common?: Common) {
const config = new Config({ common, accountCache: 10000, storageCache: 1000 })
const chain = await Chain.create({ config, blockchain })
const exec = new VMExecution({ config, chain })
await chain.open()
await exec.open()
return exec
}

0 comments on commit 4da1f03

Please sign in to comment.