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: eth get_logs not handling hex numbers #1428

Merged
merged 2 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/empty-plums-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tevm/actions": patch
---

Fixed bug in eth_getLogs not handling nubmer hex
5 changes: 5 additions & 0 deletions .changeset/nice-walls-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tevm/actions": patch
---

Fixed bug with eth_getLogs not handling numbered hex string logs well
54 changes: 3 additions & 51 deletions packages/actions/src/eth/ethGetLogsHandler.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,18 @@
import { createAddress } from '@tevm/address'
import { ForkError, InvalidBlockError } from '@tevm/errors'
import { ForkError } from '@tevm/errors'
import { createJsonRpcFetcher } from '@tevm/jsonrpc'
import { bytesToHex, hexToBigInt, hexToBytes, numberToHex } from '@tevm/utils'
import { InternalRpcError } from 'viem'
import { getPendingClient } from '../internal/getPendingClient.js'

/**
* @param {import('@tevm/blockchain').Chain} blockchain
* @param {import('../common/BlockParam.js').BlockParam} blockParam
* @returns {Promise<bigint >}
*/
const parseBlockParam = async (blockchain, blockParam) => {
if (typeof blockParam === 'number') {
return BigInt(blockParam)
}
if (typeof blockParam === 'bigint') {
return blockParam
}
if (typeof blockParam === 'string' && blockParam.startsWith('0x')) {
const block = await blockchain.getBlock(hexToBytes(/** @type {import('@tevm/utils').Hex}*/ (blockParam)))
return BigInt(block.header.number)
}
if (blockParam === 'safe') {
const safeBlock = blockchain.blocksByTag.get('safe')
// let's handle it here in case we forget to update it later
if (safeBlock) {
return safeBlock.header.number
}
throw new InvalidBlockError('safe not currently supported as block tag')
}
if (blockParam === 'latest' || blockParam === undefined) {
const safeBlock = blockchain.blocksByTag.get('latest')
// let's handle it here in case we forget to update it later
if (safeBlock) {
return safeBlock.header.number
}
throw new InvalidBlockError('latest block does not exist on chain')
}
if (blockParam === 'pending') {
// for pending we need to mine a new block and then handle it
// let's skip this functionality for now
throw new InvalidBlockError(
'Pending not yet supported but will be in future. Consider opening an issue or reaching out on telegram if you need this feature to expediate its release',
)
}
if (blockParam === 'earliest') {
return BigInt(1)
}
if (blockParam === 'finalized') {
throw new InvalidBlockError('finalized noet yet supported for this feature')
}
blockchain.logger.error({ blockParam }, 'Unknown block param pased to blockNumberHandler')
throw new InvalidBlockError(`Unknown block param ${blockParam} pased to blockNumberHandler`)
}
import { parseBlockParam } from './utils/parseBlockParam.js'

// TODO support EIP-234
/**
* @param {import('@tevm/node').TevmNode} client
* @returns {import('./EthHandler.js').EthGetLogsHandler}
*/
export const ethGetLogsHandler = (client) => async (params) => {
client.logger.debug(params, 'blockNumberHandler called with params')
client.logger.debug(params, 'ethGetLogsHandler called with params')
const vm = await client.getVm()
const receiptsManager = await client.getReceiptsManager()

Expand Down
54 changes: 54 additions & 0 deletions packages/actions/src/eth/utils/parseBlockParam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { InvalidBlockError } from '@tevm/errors'
import { hexToBigInt, hexToBytes } from '@tevm/utils'

/**
* @param {import('@tevm/blockchain').Chain} blockchain
* @param {import('../../common/BlockParam.js').BlockParam} blockParam
* @returns {Promise<bigint >}
*/
export const parseBlockParam = async (blockchain, blockParam) => {
if (typeof blockParam === 'number') {
return BigInt(blockParam)
}
if (typeof blockParam === 'bigint') {
return blockParam
}
if (typeof blockParam === 'string' && blockParam.startsWith('0x')) {
if (blockParam.length === 66) {
const block = await blockchain.getBlock(hexToBytes(/** @type {import('@tevm/utils').Hex}*/ (blockParam)))
return BigInt(block.header.number)
}
return hexToBigInt(/** @type {import('@tevm/utils').Hex}*/ (blockParam))
}
if (blockParam === 'safe') {
const safeBlock = blockchain.blocksByTag.get('safe')
// let's handle it here in case we forget to update it later
if (safeBlock) {
return safeBlock.header.number
}
throw new InvalidBlockError('safe not currently supported as block tag')
}
if (blockParam === 'latest' || blockParam === undefined) {
const safeBlock = blockchain.blocksByTag.get('latest')
// let's handle it here in case we forget to update it later
if (safeBlock) {
return safeBlock.header.number
}
throw new InvalidBlockError('latest block does not exist on chain')
}
if (blockParam === 'pending') {
// for pending we need to mine a new block and then handle it
// let's skip this functionality for now
throw new InvalidBlockError(
'Pending not yet supported but will be in future. Consider opening an issue or reaching out on telegram if you need this feature to expediate its release',
)
}
if (blockParam === 'earliest') {
return BigInt(1)
}
if (blockParam === 'finalized') {
throw new InvalidBlockError('finalized noet yet supported for this feature')
}
blockchain.logger.error({ blockParam }, 'Unknown block param pased to blockNumberHandler')
throw new InvalidBlockError(`Unknown block param ${blockParam} pased to blockNumberHandler`)
}
84 changes: 84 additions & 0 deletions packages/actions/src/eth/utils/parseBlockParam.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { InvalidBlockError } from '@tevm/errors'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { parseBlockParam } from './parseBlockParam.js'

describe('parseBlockParam', () => {
let mockBlockchain: any

beforeEach(() => {
mockBlockchain = {
getBlock: vi.fn(),
blocksByTag: new Map(),
logger: {
error: vi.fn(),
},
}
})

it('should handle number input', async () => {
const result = await parseBlockParam(mockBlockchain, 123 as any)
expect(result).toBe(123n)
})

it('should handle bigint input', async () => {
const result = await parseBlockParam(mockBlockchain, 456n)
expect(result).toBe(456n)
})

it('should handle hex block number', async () => {
mockBlockchain.getBlock.mockResolvedValue({ header: { number: 789 } })
const hash = `0x${'12'.repeat(32)}` as const
const result = await parseBlockParam(mockBlockchain, hash)
expect(result).toBe(789n)
})

it('should handle hex string block number input', async () => {
mockBlockchain.getBlock.mockResolvedValue({ header: { number: 789 } })
const result = await parseBlockParam(mockBlockchain, '0x123')
expect(result).toBe(291n)
})

it('should handle "safe" tag', async () => {
mockBlockchain.blocksByTag.set('safe', { header: { number: 101n } })
const result = await parseBlockParam(mockBlockchain, 'safe')
expect(result).toBe(101n)
})

it('should throw error for unsupported "safe" tag', async () => {
await expect(parseBlockParam(mockBlockchain, 'safe')).rejects.toThrow(InvalidBlockError)
})

it('should handle "latest" tag', async () => {
mockBlockchain.blocksByTag.set('latest', { header: { number: 202n } })
const result = await parseBlockParam(mockBlockchain, 'latest')
expect(result).toBe(202n)
})

it('should handle undefined as "latest"', async () => {
mockBlockchain.blocksByTag.set('latest', { header: { number: 303n } })
const result = await parseBlockParam(mockBlockchain, undefined as any)
expect(result).toBe(303n)
})

it('should throw error for missing "latest" block', async () => {
await expect(parseBlockParam(mockBlockchain, 'latest')).rejects.toThrow(InvalidBlockError)
})

it('should throw error for "pending" tag', async () => {
await expect(parseBlockParam(mockBlockchain, 'pending')).rejects.toThrow(InvalidBlockError)
})

it('should handle "earliest" tag', async () => {
const result = await parseBlockParam(mockBlockchain, 'earliest')
expect(result).toBe(1n)
})

it('should throw error for "finalized" tag', async () => {
await expect(parseBlockParam(mockBlockchain, 'finalized')).rejects.toThrow(InvalidBlockError)
})

it('should throw error for unknown block param', async () => {
await expect(parseBlockParam(mockBlockchain, 'unknown' as any)).rejects.toThrow(InvalidBlockError)
expect(mockBlockchain.logger.error).toHaveBeenCalled()
})
})
Loading