Skip to content

Commit

Permalink
🐛 fix: eth get_logs not handling hex numbers (#1428)
Browse files Browse the repository at this point in the history
## Description

_Concise description of proposed changes_

## Testing

Explain the quality checks that have been done on the code changes

## Additional Information

- [ ] I read the [contributing docs](../docs/contributing.md) (if this
is your first contribution)

Your ENS/address:

---------

Co-authored-by: William Cory <[email protected]>
  • Loading branch information
roninjin10 and William Cory authored Sep 12, 2024
1 parent 989a334 commit bc00e14
Show file tree
Hide file tree
Showing 5 changed files with 151 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`)
}
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()
})
})

0 comments on commit bc00e14

Please sign in to comment.