Skip to content

Commit

Permalink
🐛 Fix: eth_getLogs not hadnling hex numbers correct
Browse files Browse the repository at this point in the history
  • Loading branch information
William Cory authored and William Cory committed Sep 12, 2024
1 parent 2b20a13 commit ed55deb
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 51 deletions.
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`)
}
85 changes: 85 additions & 0 deletions packages/actions/src/eth/utils/parseBlockParam.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { InvalidBlockError } from '@tevm/errors'
import { hexToBigInt } from 'viem'
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()
})
})

0 comments on commit ed55deb

Please sign in to comment.