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

fix(blockchain): Throw nicer error in putBlock #3326

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

roninjin10
Copy link
Collaborator

Error message was confusing for putBlock

invalid header. Less values than expected were received. Min: 15, got: 0

This catches the error and throws a more informative error message

Error message was confusing for putBlock

```
invalid header. Less values than expected were received. Min: 15, got: 0
```

This catches the error and throws a more informative error message
@roninjin10 roninjin10 marked this pull request as draft March 18, 2024 06:21
@jochem-brouwer
Copy link
Member

jochem-brouwer commented Mar 18, 2024

In what context did you hit this error?

@roninjin10
Copy link
Collaborator Author

roninjin10 commented Mar 18, 2024

// This is just an ethereumjs block
import { TevmBlock } from './TevmBlock.js'
// This is just
// import {Blockchain} from '@ethereumjs/blockchain'
// export TevmBlockchain extends Blockchain {}
import { TevmBlockchain } from './TevmBlockchain.js'
import { genesisStateRoot } from '@ethereumjs/trie'
// This is just a util that peforms a json-rpc request
import { createJsonRpcFetcher } from '@tevm/jsonrpc'
// these are all from ethereumjs or viem
import { bytesToHex, createMemoryDb, numberToHex, parseGwei } from '@tevm/utils'
import { DBOp, DBSetBlockOrHeader, DBSetHashToNumber } from '@ethereumjs/blockchain'
// This import is private to ethereumjs/blockchain package
import { DBTarget } from './ethjs/db/operation.js'

// TODO user should be able to pass in header information to json rpc requests anywhere createJsonRpcFetcher is used

const OP_DEPOSIT_TX_TYPE = '0x7e'

/**
 * @param {object} options
 * @param {import('@ethereumjs/common').Common} options.common
 * @param {string | undefined} options.forkUrl
 * @param {import('@tevm/utils').BlockTag | import('@tevm/utils').Hex | bigint} options.blockTag
 * @returns {Promise<TevmBlockchain>}
 */
export const createBlockchain = async ({
	common,
	forkUrl,
	blockTag = 'latest',
}) => {
	const db = createMemoryDb()
	/**
	 * @type {import('@ethereumjs/util').GenesisState}
	 */
	const genesisState = {}

	const genesisBlock = await (() => {
		if (forkUrl) {
			return createJsonRpcFetcher(forkUrl).request({
				jsonrpc: '2.0',
				id: 1,
				method: 'eth_getBlockByNumber',
				params: [
					'0x0',
					true
				],
			}).then(response => {
				return TevmBlock.fromRPC(/** @type {any}*/(response.result), undefined, {
					common,
					freeze: true,
					setHardfork: false,
					skipConsensusFormatValidation: true,
				})
			})
		}
		return TevmBlock.fromBlockData(
			{
				header: {
					...common.genesis(),
					baseFeePerGas: parseGwei('1'),
					coinbase: '0xc014ba5ec014ba5ec014ba5ec014ba5ec014ba5e',
				},
				...(common.isActivatedEIP(4895)
					? {
						withdrawals:
							/** @type {Array<import('@ethereumjs/util').WithdrawalData>}*/ ([]),
					}
					: {}),
			},
			{ common, setHardfork: false, skipConsensusFormatValidation: true },
		)
	})()
	const stateRoot = await genesisStateRoot(genesisState)

	const out = await TevmBlockchain.create({
		genesisState,
		hardforkByHeadBlockNumber: false,
		db,
		common,
		validateBlocks: false,
		validateConsensus: false,
		genesisBlock,
		genesisStateRoot: stateRoot,
		// using ethereumjs defaults for this and disabling it
		// consensus,
	})

	if (forkUrl) {
		/**
		 * @param {bigint | import('@tevm/utils').BlockTag | import('@tevm/utils').Hex} blockTag}
		 */
		const blockFromRpc = async blockTag => {
			// TODO we shoudl validate this
			const { result: jsonRpcBlock } = await createJsonRpcFetcher(forkUrl).request({
				jsonrpc: '2.0',
				id: 1,
				method: 'eth_getBlockByNumber',
				params: [
					typeof blockTag === 'bigint' ? numberToHex(blockTag) : blockTag,
					true,
				],
			});
			console.log({ jsonRpcBlock })

			return TevmBlock.fromRPC({
				.../** @type {any}*/(jsonRpcBlock),
				// as a hack until optimism deposit tx type is supported filter out all those tx
				parentHash: genesisBlock.hash(),
				transactions: /** @type {any}*/(jsonRpcBlock).transactions.filter(
					/** @param {any} tx */
					tx => tx.type !== OP_DEPOSIT_TX_TYPE,
				)
			}, undefined, {
				common,
				freeze: true,
				setHardfork: false,
				skipConsensusFormatValidation: true,
			})
		}
		const forkedBlock = await blockFromRpc(blockTag)
		// await out.putBlock(forkedBlock)
		await out.dbManager.batch([
			...DBSetBlockOrHeader(forkedBlock),
			DBSetHashToNumber(forkedBlock.hash(), forkedBlock.header.number),
			DBOp.set(DBTarget.Heads, {
				latest: bytesToHex(forkedBlock.hash()),
				pending: bytesToHex(forkedBlock.hash()),
			}),
		])

		/**
		 * @type {any}
		 */
		const outAny = out

		outAny._headBlockHash = forkedBlock.hash()
		outAny._headHeaderHash = forkedBlock.header.hash()
		outAny._heads = {
			...outAny._heads,
			latest: forkedBlock.hash(),
			pending: forkedBlock.hash(),
		}
	}

	console.log('successfully created blockchain with genesis block and state root')

	return out
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants