Skip to content

Commit

Permalink
Merge branch 'master' of github.com:ethereumjs/ethereumjs-monorepo in…
Browse files Browse the repository at this point in the history
…to ethjs-repl
  • Loading branch information
scorbajio committed Dec 16, 2024
2 parents 2e4733a + 4da1f03 commit 50ecdfd
Show file tree
Hide file tree
Showing 15 changed files with 198 additions and 80 deletions.
17 changes: 11 additions & 6 deletions packages/client/src/rpc/modules/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,23 +462,28 @@ export class Debug {
}

/**
* Returns an RLP-encoded block
* @param blockOpt Block number or tag
* 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 yet supported`,
message: `"pending" is not supported`,
}
}

const block = await getBlockByOption(blockOpt, this.chain)
let res
try {
res = await this.service.skeleton?.setHead(block)
await this.service.skeleton?.setHead(block, true)
await this.service.execution.setHead([block])
} catch (e) {
console.log(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
}
4 changes: 4 additions & 0 deletions packages/common/src/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ export const Mainnet: ChainConfig = {
name: 'prague',
block: null,
},
{
name: 'osaka',
block: null,
},
],
bootstrapNodes: [
{
Expand Down
1 change: 1 addition & 0 deletions packages/common/src/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export enum Hardfork {
Shanghai = 'shanghai',
Cancun = 'cancun',
Prague = 'prague',
Osaka = 'osaka',
Verkle = 'verkle',
}

Expand Down
5 changes: 3 additions & 2 deletions packages/common/src/hardforks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,11 @@ export const hardforksDict: HardforksDict = {
* Status : Final
*/
prague: {
// TODO update this accordingly to the right devnet setup
//eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // This is EOF-only
eips: [2537, 2935, 6110, 7002, 7251, 7685, 7702], // This is current prague without EOF
},
osaka: {
eips: [663, 3540, 3670, 4200, 4750, 5450, 6206, 7069, 7480, 7620, 7692, 7698], // These are the EOF EIPs
},
/**
* Description: Next feature hardfork after prague, internally used for verkle testing/implementation (incomplete/experimental)
* URL : https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/osaka.md
Expand Down
4 changes: 4 additions & 0 deletions packages/evm/src/eof/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ class EOFBody {
}
} else {
dataSection = stream.readRemainder()

if (dataSection.length > header.dataSize) {
validationError(EOFError.DanglingBytes)
}
}

// Write all data to the object
Expand Down
1 change: 1 addition & 0 deletions packages/evm/src/eof/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export enum EOFError {
InvalidStackHeight = 'invalid stack height',
InvalidJUMPF = 'invalid jumpf target (output count)',
InvalidReturningSection = 'invalid returning code section: section is not returning',
ReturningNoReturn = 'invalid section: section should return but has no RETF/JUMP to return',
RJUMPVTableSize0 = 'invalid RJUMPV: table size 0',
UnreachableCodeSections = 'unreachable code sections',
UnreachableCode = 'unreachable code (by forward jumps)',
Expand Down
75 changes: 44 additions & 31 deletions packages/evm/src/eof/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,31 +61,9 @@ function validateOpcodes(
evm: EVM,
mode: ContainerSectionType = ContainerSectionType.RuntimeCode,
) {
// Track the intermediate bytes
const intermediateBytes = new Set<number>()
// Track the jump locations (for forward jumps it is unknown at the first pass if the byte is intermediate)
const jumpLocations = new Set<number>()

// Track the type of the container targets
// Should at the end of the analysis have all the containers
const containerTypeMap = new Map<number, ContainerSectionType>()

function addJump(location: number) {
if (intermediateBytes.has(location)) {
// When trying to JUMP into an intermediate byte: this is invalid
validationError(EOFError.InvalidRJUMP)
}
jumpLocations.add(location)
}

function addIntermediate(location: number) {
if (jumpLocations.has(location)) {
// When trying to add an intermediate to a location already JUMPed to: this is invalid
validationError(EOFError.InvalidRJUMP)
}
intermediateBytes.add(location)
}

// TODO (?) -> stackDelta currently only has active EOF opcodes, can use it directly (?)
// (so no need to generate the valid opcodeNumbers)

Expand Down Expand Up @@ -156,11 +134,42 @@ function validateOpcodes(

let codeSection = -1
for (const code of container.body.codeSections) {
// Track the intermediate bytes
const intermediateBytes = new Set<number>()
// Track the jump locations (for forward jumps it is unknown at the first pass if the byte is intermediate)
const jumpLocations = new Set<number>()

// eslint-disable-next-line no-inner-declarations
function addJump(location: number) {
if (intermediateBytes.has(location)) {
// When trying to JUMP into an intermediate byte: this is invalid
validationError(EOFError.InvalidRJUMP)
}
jumpLocations.add(location)
}

// eslint-disable-next-line no-inner-declarations
function addIntermediate(location: number) {
if (jumpLocations.has(location)) {
// When trying to add an intermediate to a location already JUMPed to: this is invalid
validationError(EOFError.InvalidRJUMP)
}
intermediateBytes.add(location)
}

codeSection++

reachableSections[codeSection] = new Set()

const returningFunction = container.body.typeSections[codeSection].outputs === 0x80
// Section is marked as "non-returning": it does never "return" to another code section
// it rather exits the current EVM call frame
const nonReturningFunction = container.body.typeSections[codeSection].outputs === 0x80

// Boolean flag to mark if this section has a returning opcode:
// RETF
// Or JUMPF into a returning section
// Each returning section should contain a returning opcode
let sectionHasReturningOpcode = false

// Tracking set of reachable opcodes
const reachableOpcodes = new Set<number>()
Expand Down Expand Up @@ -212,12 +221,12 @@ function validateOpcodes(
let minStackNext = minStackCurrent + delta
let maxStackNext = maxStackCurrent + delta

if (maxStackNext > 1023) {
if (maxStackNext > 1024) {
// TODO verify if 1023 or 1024 is the right constant
validationError(EOFError.StackOverflow)
}

if (returningFunction && opcode === 0xe4) {
if (nonReturningFunction && opcode === 0xe4) {
validationError(EOFError.InvalidReturningSection)
}

Expand Down Expand Up @@ -328,7 +337,7 @@ function validateOpcodes(
validationError(EOFError.InvalidJUMPF)
}

if (returningFunction && targetOutputs <= 0x7f) {
if (nonReturningFunction && targetOutputs <= 0x7f) {
// Current function is returning, but target is not, cannot jump into this
validationError(EOFError.InvalidReturningSection)
}
Expand All @@ -344,12 +353,12 @@ function validateOpcodes(
if (!(minStackCurrent === maxStackCurrent && maxStackCurrent === expectedStack)) {
validationError(EOFError.InvalidStackHeight)
}
sectionHasReturningOpcode = true
}
if (
maxStackCurrent + container.body.typeSections[target].maxStackHeight - targetInputs >
1024
) {
//console.log(maxStackCurrent, targetOutputs, targetInputs, targetNonReturning)
validationError(EOFError.StackOverflow)
}
}
Expand All @@ -360,6 +369,7 @@ function validateOpcodes(
if (!(minStackCurrent === maxStackCurrent && maxStackCurrent === outputs)) {
validationError(EOFError.InvalidStackHeight)
}
sectionHasReturningOpcode = true
} else if (opcode === 0xe6) {
// DUPN
const toDup = code[ptr + 1]
Expand All @@ -369,9 +379,7 @@ function validateOpcodes(
} else if (opcode === 0xe7) {
// SWAPN
const toSwap = code[ptr + 1]
// TODO: EVMONEs test wants this to be `toSwap + 2`, but that seems to be incorrect
// Will keep `toSwap + 1` for now
if (toSwap + 1 > minStackCurrent) {
if (toSwap + 2 > minStackCurrent) {
validationError(EOFError.StackUnderflow)
}
} else if (opcode === 0xe8) {
Expand Down Expand Up @@ -485,10 +493,15 @@ function validateOpcodes(
if (container.body.typeSections[codeSection].maxStackHeight !== maxStackHeight) {
validationError(EOFError.MaxStackHeightViolation)
}
if (maxStackHeight > 1023) {
if (maxStackHeight > 1024) {
// TODO verify if 1023 or 1024 is the right constant
validationError(EOFError.MaxStackHeightLimit)
}

// Validate that if the section is returning, there is a returning opcode
if (!sectionHasReturningOpcode && !nonReturningFunction) {
validationError(EOFError.ReturningNoReturn)
}
}

// Verify that each code section can be reached from code section 0
Expand Down
1 change: 1 addition & 0 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export class EVM implements EVMInterface {
Hardfork.Shanghai,
Hardfork.Cancun,
Hardfork.Prague,
Hardfork.Osaka,
Hardfork.Verkle,
]
protected _tx?: {
Expand Down
Loading

0 comments on commit 50ecdfd

Please sign in to comment.