From 87b6ca6fb9d00f230c53cade832368d0b1fa901d Mon Sep 17 00:00:00 2001 From: Shunji Zhan Date: Fri, 19 Apr 2024 16:57:40 +0800 Subject: [PATCH] get logs from cache (#985) * get logs from cache * add cache * polish logging * fix naming --- .../__tests__/e2e/json-rpc-provider.test.ts | 21 ++++++++ packages/eth-providers/src/base-provider.ts | 48 ++++++++++++++----- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/packages/eth-providers/src/__tests__/e2e/json-rpc-provider.test.ts b/packages/eth-providers/src/__tests__/e2e/json-rpc-provider.test.ts index 69898f02..2517b74f 100644 --- a/packages/eth-providers/src/__tests__/e2e/json-rpc-provider.test.ts +++ b/packages/eth-providers/src/__tests__/e2e/json-rpc-provider.test.ts @@ -174,6 +174,27 @@ describe('JsonRpcProvider', async () => { }); }); + describe('get logs without subql', () => { + it('works', async () => { + const echoFactory = new ContractFactory(echoJson.abi, echoJson.bytecode, wallet); + const echo = await echoFactory.deploy(); + + const { blockNumber: block0 } = await (await echo.scream('hello Gogeta!')).wait(); + let logs = await wallet.provider.getLogs({ + address: echo.address, + }); + expect(logs.length).to.eq(1); + + const { blockNumber: block1 } = await (await echo.scream('hello Vegito!')).wait(); + logs = await wallet.provider.getLogs({ + address: echo.address, + fromBlock: block0, + toBlock: block1, + }); + expect(logs.length).to.eq(2); + }); + }); + describe('subscription', () => { it('subscribe to new block', async () => { const curBlockNumber = await provider.getBlockNumber(); diff --git a/packages/eth-providers/src/base-provider.ts b/packages/eth-providers/src/base-provider.ts index 16d5d4cc..11784d5e 100644 --- a/packages/eth-providers/src/base-provider.ts +++ b/packages/eth-providers/src/base-provider.ts @@ -1795,17 +1795,26 @@ export abstract class BaseProvider extends AbstractProvider { _getSubqlMissedLogs = async (toBlock: number, filter: SanitizedLogFilter): Promise => { const targetBlock = Math.min(toBlock, await this.finalizedBlockNumber); // subql upperbound is finalizedBlockNumber const lastProcessedHeight = await this.subql.getLastProcessedHeight(); - const missedBlockCount = targetBlock - lastProcessedHeight; - if (missedBlockCount <= 0) return []; - const firstMissedHeight = lastProcessedHeight + 1; - const missedHeights = Array.from( - { length: missedBlockCount }, - (_, i) => firstMissedHeight + i, + + return this._getLogsFromCache(firstMissedHeight, targetBlock, filter); + }; + + _getLogsFromCache = async ( + fromBlock: number, + toBlock: number, + filter: SanitizedLogFilter, + ): Promise => { + const blockCount = toBlock - fromBlock + 1; // when to === from, it should return 1 block + if (blockCount <= 0) return []; + + const heights = Array.from( + { length: blockCount }, + (_, i) => fromBlock + i, ); - const missedBlockHashes = await Promise.all(missedHeights.map(this._getBlockHash.bind(this))); + const blockHashes = await Promise.all(heights.map(this._getBlockHash.bind(this))); - return missedBlockHashes + return blockHashes .map(this.blockCache.getLogsAtBlock.bind(this)) .flat() .filter(log => filterLogByBlockNumber(log, filter.fromBlock, filter.toBlock)) @@ -1814,13 +1823,28 @@ export abstract class BaseProvider extends AbstractProvider { // Bloom-filter Queries getLogs = async (rawFilter: LogFilter): Promise => { + const filter = await this._sanitizeRawFilter(rawFilter); + if (!this.subql) { - return logger.throwError( - 'missing subql url to fetch logs, to initialize base provider with subql, please provide a subqlUrl param.' + const _throwErr = (earliestCachedBlockNumber?: number) => logger.throwError( + 'cache does not contain enough info to fetch requested logs, please reduce block range or initialize provider with a subql url', + Logger.errors.SERVER_ERROR, + { + requestFromBlock: filter.fromBlock, + earliestCachedBlockNumber, + }, ); - } - const filter = await this._sanitizeRawFilter(rawFilter); + const earliestCachedBlockHash = this.blockCache.cachedBlockHashes[0]; + if (!earliestCachedBlockHash) return _throwErr(); + + const earliestCachedBlockNumber = await this._getBlockNumber(earliestCachedBlockHash); + const isAllTargetLogsInCache = earliestCachedBlockNumber <= filter.fromBlock; + + return isAllTargetLogsInCache + ? this._getLogsFromCache(filter.fromBlock, filter.toBlock, filter) + : _throwErr(earliestCachedBlockNumber); + } // only filter by blockNumber and address, since topics are filtered at last const [subqlLogs, extraLogs] = await Promise.all([