From aaeebe0b81b5f9c5b2f965f18a4db8273904b77b Mon Sep 17 00:00:00 2001 From: SanLeo461 Date: Fri, 9 May 2025 16:25:29 +1000 Subject: [PATCH 1/2] client: add blockTimestamp to eth_getLogs, add tests --- packages/client/src/rpc/modules/eth.ts | 2 + packages/client/test/rpc/eth/getLogs.spec.ts | 58 +++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/packages/client/src/rpc/modules/eth.ts b/packages/client/src/rpc/modules/eth.ts index 7acd32f8571..9f5e60a06a8 100644 --- a/packages/client/src/rpc/modules/eth.ts +++ b/packages/client/src/rpc/modules/eth.ts @@ -100,6 +100,7 @@ type JSONRPCLog = { transactionHash: string | null // DATA, 32 Bytes - hash of the transactions this log was created from. null when it's pending. blockHash: string | null // DATA, 32 Bytes - hash of the block where this log was in. null when it's pending. blockNumber: string | null // QUANTITY - the block number where this log was in. null when it's pending. + blockTimestamp: string | null // QUANTITY - the block timestamp where this log was in. null when it's pending. address: string // DATA, 20 Bytes - address from which this log originated. data: string // DATA - contains one or more 32 Bytes non-indexed arguments of the log. topics: string[] // Array of DATA - Array of 0 to 4 32 Bytes DATA of indexed log arguments. @@ -174,6 +175,7 @@ const toJSONRPCLog = async ( transactionHash: tx !== undefined ? bytesToHex(tx.hash()) : null, blockHash: block ? bytesToHex(block.hash()) : null, blockNumber: block ? bigIntToHex(block.header.number) : null, + blockTimestamp: block ? bigIntToHex(block.header.timestamp) : null, address: bytesToHex(log[0]), topics: log[1].map(bytesToHex), data: bytesToHex(log[2]), diff --git a/packages/client/test/rpc/eth/getLogs.spec.ts b/packages/client/test/rpc/eth/getLogs.spec.ts index a8f81e35681..053dadf454c 100644 --- a/packages/client/test/rpc/eth/getLogs.spec.ts +++ b/packages/client/test/rpc/eth/getLogs.spec.ts @@ -1,5 +1,11 @@ import { createLegacyTx } from '@ethereumjs/tx' -import { bytesToHex, createContractAddress, hexToBytes } from '@ethereumjs/util' +import { + bigIntToHex, + bytesToHex, + createContractAddress, + hexToBigInt, + hexToBytes, +} from '@ethereumjs/util' import { assert, describe, it } from 'vitest' import { INVALID_PARAMS } from '../../../src/rpc/error-code.ts' @@ -75,7 +81,7 @@ describe(method, async () => { { common }, ).sign(dummy.privKey) - await runBlockWithTxs(chain, execution, [tx1, tx2, tx3, tx4]) + let block = await runBlockWithTxs(chain, execution, [tx1, tx2, tx3, tx4]) // compare the logs let res = await rpc.request(method, [{ fromBlock: 'earliest', toBlock: 'latest' }]) @@ -100,6 +106,17 @@ describe(method, async () => { assert.fail(`should return the correct logs (fromBlock/toBlock as 'earliest' and 'latest')`) } + if (hexToBigInt(res.result[0].blockTimestamp) === block.header.timestamp) { + assert.isTrue( + true, + `should return the correct blockTimestamp (fromBlock/toBlock as 'earliest' and 'latest')`, + ) + } else { + assert.fail( + `should return the correct blockTimestamp (fromBlock/toBlock as 'earliest' and 'latest')`, + ) + } + // get the logs using fromBlock/toBlock as numbers res = await rpc.request(method, [{ fromBlock: '0x0', toBlock: '0x1' }]) assert.strictEqual( @@ -206,6 +223,43 @@ describe(method, async () => { 20, 'should return the correct logs (filter by blockHash)', ) + + // test adding more logs and checking timestamps against multiple blocks + const tx5 = createLegacyTx( + { + ...txData, + data, + to: contractAddr1, + nonce: 4, + }, + { common }, + ).sign(dummy.privKey) + const tx6 = createLegacyTx( + { + ...txData, + data, + to: contractAddr2, + nonce: 5, + }, + { common }, + ).sign(dummy.privKey) + + const oldTimestamp = bigIntToHex(block.header.timestamp) + block = await runBlockWithTxs(chain, execution, [tx5, tx6]) + const newTimestamp = bigIntToHex(block.header.timestamp) + + res = await rpc.request(method, [{ fromBlock: 'earliest', toBlock: 'latest' }]) + + if ( + res.result[0].blockTimestamp === oldTimestamp && + res.result[10].blockTimestamp === oldTimestamp && + res.result[20].blockTimestamp === newTimestamp && + res.result[30].blockTimestamp === newTimestamp + ) { + assert.isTrue(true, 'should return the correct log timestamps across multiple blocks') + } else { + assert.fail('should return the correct log timestamps across multiple blocks') + } }) it('call with invalid params', async () => { From 00f530a638a6114fd370e7fa03643518d7bc2966 Mon Sep 17 00:00:00 2001 From: SanLeo461 Date: Sat, 10 May 2025 17:16:53 +1000 Subject: [PATCH 2/2] client: cleanup getLogs blocktimestamp tests --- packages/client/test/rpc/eth/getLogs.spec.ts | 36 ++++++++------------ 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/packages/client/test/rpc/eth/getLogs.spec.ts b/packages/client/test/rpc/eth/getLogs.spec.ts index 053dadf454c..088cc826b64 100644 --- a/packages/client/test/rpc/eth/getLogs.spec.ts +++ b/packages/client/test/rpc/eth/getLogs.spec.ts @@ -106,16 +106,11 @@ describe(method, async () => { assert.fail(`should return the correct logs (fromBlock/toBlock as 'earliest' and 'latest')`) } - if (hexToBigInt(res.result[0].blockTimestamp) === block.header.timestamp) { - assert.isTrue( - true, - `should return the correct blockTimestamp (fromBlock/toBlock as 'earliest' and 'latest')`, - ) - } else { - assert.fail( - `should return the correct blockTimestamp (fromBlock/toBlock as 'earliest' and 'latest')`, - ) - } + assert.strictEqual( + hexToBigInt(res.result[0].blockTimestamp), + block.header.timestamp, + `should return the correct blockTimestamp (fromBlock/toBlock as 'earliest' and 'latest')`, + ) // get the logs using fromBlock/toBlock as numbers res = await rpc.request(method, [{ fromBlock: '0x0', toBlock: '0x1' }]) @@ -244,22 +239,19 @@ describe(method, async () => { { common }, ).sign(dummy.privKey) - const oldTimestamp = bigIntToHex(block.header.timestamp) + const block1Timestamp = bigIntToHex(block.header.timestamp) block = await runBlockWithTxs(chain, execution, [tx5, tx6]) - const newTimestamp = bigIntToHex(block.header.timestamp) + const block2Timestamp = bigIntToHex(block.header.timestamp) res = await rpc.request(method, [{ fromBlock: 'earliest', toBlock: 'latest' }]) + const block1Logs: any[] = res.result.filter((log: any) => log.blockNumber === '0x1') + const block2Logs: any[] = res.result.filter((log: any) => log.blockNumber === '0x2') - if ( - res.result[0].blockTimestamp === oldTimestamp && - res.result[10].blockTimestamp === oldTimestamp && - res.result[20].blockTimestamp === newTimestamp && - res.result[30].blockTimestamp === newTimestamp - ) { - assert.isTrue(true, 'should return the correct log timestamps across multiple blocks') - } else { - assert.fail('should return the correct log timestamps across multiple blocks') - } + assert.isTrue( + block1Logs.every((log) => log.blockTimestamp === block1Timestamp) && + block2Logs.every((log) => log.blockTimestamp === block2Timestamp), + 'should return the correct log timestamps across multiple blocks', + ) }) it('call with invalid params', async () => {