Skip to content

Commit 84cb467

Browse files
AlfredoG87acuarica
andauthored
chore: cherry-pick #2236 into release 0.44 (#2261)
feat: remove block range limit in `eth_getLogs` when filtering with a single address (#2236) * Fix address type * Remove block range limit when filtering with a single address * Fix single address condition * Add unit tests * Add acceptance test for large block range with a single address * Fix acceptance test * Update `eth_getLogs`'s openrpc docs summary and restrictions * Remove unneeded `console.log` * Remove `env.TEST` usage as suggested by Fredy * Improve test titles to use `it should` format as suggested by Fredy * Include more details in the openrpc `eth_getLogs` description --------- Signed-off-by: Luis Mastrangelo <[email protected]> Signed-off-by: Alfredo Gutierrez <[email protected]> Co-authored-by: Luis Mastrangelo <[email protected]>
1 parent fe180eb commit 84cb467

File tree

6 files changed

+111
-34
lines changed

6 files changed

+111
-34
lines changed

docs/openrpc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,8 @@
401401
},
402402
{
403403
"name": "eth_getLogs",
404-
"summary": "Returns an array of all logs matching filter with given id.",
404+
"summary": "Returns an array of all logs matching a given filter object.",
405+
"description": "The block range filter, _i.e._, `fromBlock` and `toBlock` arguments, cannot be larger than `ETH_GET_LOGS_BLOCK_RANGE_LIMIT` (defaults to `1000`). However, when `address` represents a single address, either a `string` or an array with a single element, this restriction is lifted.\n\nWhen the logs for an individual address exceeds the Mirror Node page limit `MIRROR_NODE_CONTRACT_RESULTS_LOGS_PG_MAX` (defaults to `200`) an error `-32011` _Mirror Node pagination count range too large_ is returned.",
405406
"params": [
406407
{
407408
"name": "Filter",

packages/relay/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ export interface Eth {
8989
blockHash: string | null,
9090
fromBlock: string | null,
9191
toBlock: string | null,
92-
address: string | null,
92+
address: string | string[] | null,
9393
topics: any[] | null,
9494
requestId?: string,
9595
): Promise<Log[]>;

packages/relay/src/lib/eth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2287,7 +2287,7 @@ export class EthImpl implements Eth {
22872287
blockHash: string | null,
22882288
fromBlock: string | 'latest',
22892289
toBlock: string | 'latest',
2290-
address: string | [string] | null,
2290+
address: string | string[] | null,
22912291
topics: any[] | null,
22922292
requestIdPrefix?: string,
22932293
): Promise<Log[]> {

packages/relay/src/lib/services/ethService/ethCommonService/index.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export class CommonService implements ICommonService {
7474
'ETH_BLOCK_NUMBER_CACHE_TTL_MS_DEFAULT',
7575
);
7676

77+
private getLogsBlockRangeLimit() {
78+
return parseNumericEnvVar('ETH_GET_LOGS_BLOCK_RANGE_LIMIT', 'DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT');
79+
}
80+
7781
constructor(mirrorNodeClient: MirrorNodeClient, logger: Logger, cacheService: CacheService) {
7882
this.mirrorNodeClient = mirrorNodeClient;
7983
this.logger = logger;
@@ -99,12 +103,8 @@ export class CommonService implements ICommonService {
99103
fromBlock: string,
100104
toBlock: string,
101105
requestIdPrefix?: string,
106+
address?: string | string[] | null,
102107
) {
103-
const blockRangeLimit =
104-
process.env.TEST === 'true'
105-
? constants.DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT
106-
: Number(process.env.ETH_GET_LOGS_BLOCK_RANGE_LIMIT);
107-
108108
if (this.blockTagIsLatestOrPending(toBlock)) {
109109
toBlock = CommonService.blockLatest;
110110
}
@@ -141,7 +141,15 @@ export class CommonService implements ICommonService {
141141

142142
if (fromBlockNum > toBlockNum) {
143143
return false;
144-
} else if (toBlockNum - fromBlockNum > blockRangeLimit) {
144+
}
145+
146+
const blockRangeLimit = this.getLogsBlockRangeLimit();
147+
// Increasing it to more then one address may degrade mirror node performance
148+
// when addresses contains many log events.
149+
const isSingleAddress = Array.isArray(address)
150+
? address.length === 1
151+
: typeof address === 'string' && address !== '';
152+
if (!isSingleAddress && toBlockNum - fromBlockNum > blockRangeLimit) {
145153
throw predefined.RANGE_TOO_LARGE(blockRangeLimit);
146154
}
147155
}
@@ -268,7 +276,7 @@ export class CommonService implements ICommonService {
268276
}
269277
}
270278

271-
public async getLogsByAddress(address: string | [string], params: any, requestIdPrefix) {
279+
public async getLogsByAddress(address: string | string[], params: any, requestIdPrefix) {
272280
const addresses = Array.isArray(address) ? address : [address];
273281
const logPromises = addresses.map((addr) =>
274282
this.mirrorNodeClient.getContractResultsLogsByAddress(addr, params, undefined, requestIdPrefix),
@@ -283,7 +291,7 @@ export class CommonService implements ICommonService {
283291
return logs;
284292
}
285293

286-
public async getLogsWithParams(address: string | [string] | null, params, requestIdPrefix?: string): Promise<Log[]> {
294+
public async getLogsWithParams(address: string | string[] | null, params, requestIdPrefix?: string): Promise<Log[]> {
287295
const EMPTY_RESPONSE = [];
288296

289297
let logResults;
@@ -321,7 +329,7 @@ export class CommonService implements ICommonService {
321329
blockHash: string | null,
322330
fromBlock: string | 'latest',
323331
toBlock: string | 'latest',
324-
address: string | [string] | null,
332+
address: string | string[] | null,
325333
topics: any[] | null,
326334
requestIdPrefix?: string,
327335
): Promise<Log[]> {
@@ -332,7 +340,9 @@ export class CommonService implements ICommonService {
332340
if (!(await this.validateBlockHashAndAddTimestampToParams(params, blockHash, requestIdPrefix))) {
333341
return EMPTY_RESPONSE;
334342
}
335-
} else if (!(await this.validateBlockRangeAndAddTimestampToParams(params, fromBlock, toBlock, requestIdPrefix))) {
343+
} else if (
344+
!(await this.validateBlockRangeAndAddTimestampToParams(params, fromBlock, toBlock, requestIdPrefix, address))
345+
) {
336346
return EMPTY_RESPONSE;
337347
}
338348

packages/relay/tests/lib/eth/eth_getLogs.spec.ts

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,45 @@ describe('@ethGetLogs using MirrorNode', async function () {
281281
expectLogData3(result[2]);
282282
});
283283

284+
[CONTRACT_ADDRESS_1, [CONTRACT_ADDRESS_1]].forEach((address) => {
285+
it(`should filter logs by \`${JSON.stringify(address)}\` with a large block range`, async function () {
286+
const filteredLogs = {
287+
logs: [DEFAULT_LOGS.logs[0], DEFAULT_LOGS.logs[1], DEFAULT_LOGS.logs[2]],
288+
};
289+
restMock.onGet(CONTRACTS_LOGS_WITH_FILTER).reply(200, filteredLogs);
290+
for (const log of filteredLogs.logs) {
291+
restMock.onGet(`contracts/${log.address}`).reply(200, DEFAULT_CONTRACT);
292+
}
293+
294+
const fromBlock = {
295+
...DEFAULT_BLOCK,
296+
number: 1,
297+
};
298+
const toBlock = {
299+
...DEFAULT_BLOCK,
300+
number: 1003,
301+
};
302+
303+
const blockBeyondMaximumRange = {
304+
...DEFAULT_BLOCK,
305+
number: 1007,
306+
};
307+
308+
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [blockBeyondMaximumRange] });
309+
restMock.onGet('blocks/1').reply(200, fromBlock);
310+
restMock.onGet('blocks/1003').reply(200, toBlock);
311+
312+
const result = await ethImpl.getLogs(null, '0x1', '0x3eb', address, null);
313+
314+
expect(result).to.exist;
315+
316+
expect(result.length).to.eq(3);
317+
expectLogData1(result[0]);
318+
expectLogData2(result[1]);
319+
expectLogData3(result[2]);
320+
});
321+
});
322+
284323
it('multiple addresses filter', async function () {
285324
const filteredLogsAddress1 = {
286325
logs: [DEFAULT_LOGS.logs[0], DEFAULT_LOGS.logs[1], DEFAULT_LOGS.logs[2]],
@@ -439,27 +478,29 @@ describe('@ethGetLogs using MirrorNode', async function () {
439478
expectLogData1(result[0]);
440479
});
441480

442-
it('when block range is too large', async function () {
443-
const fromBlock = {
444-
...DEFAULT_BLOCK,
445-
number: 1,
446-
};
447-
const toBlock = {
448-
...DEFAULT_BLOCK,
449-
number: 1003,
450-
};
451-
452-
const blockBeyondMaximumRange = {
453-
...DEFAULT_BLOCK,
454-
number: 1007,
455-
};
456-
457-
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [blockBeyondMaximumRange] });
458-
restMock.onGet('blocks/1').reply(200, fromBlock);
459-
restMock.onGet('blocks/1003').reply(200, toBlock);
460-
461-
await ethGetLogsFailing(ethImpl, [null, '0x1', '0x3eb', null, null], (error) => {
462-
expect(error.message).to.equal('Exceeded maximum block range: 1000');
481+
[null, [], [CONTRACT_ADDRESS_1, CONTRACT_ADDRESS_2]].forEach((address) => {
482+
it(`should fail when block range is too large for address(es) \`${JSON.stringify(address)}\``, async function () {
483+
const fromBlock = {
484+
...DEFAULT_BLOCK,
485+
number: 1,
486+
};
487+
const toBlock = {
488+
...DEFAULT_BLOCK,
489+
number: 1003,
490+
};
491+
492+
const blockBeyondMaximumRange = {
493+
...DEFAULT_BLOCK,
494+
number: 1007,
495+
};
496+
497+
restMock.onGet(BLOCKS_LIMIT_ORDER_URL).reply(200, { blocks: [blockBeyondMaximumRange] });
498+
restMock.onGet('blocks/1').reply(200, fromBlock);
499+
restMock.onGet('blocks/1003').reply(200, toBlock);
500+
501+
await ethGetLogsFailing(ethImpl, [null, '0x1', '0x3eb', address, null], (error) => {
502+
expect(error.message).to.equal('Exceeded maximum block range: 1000');
503+
});
463504
});
464505
});
465506

packages/server/tests/acceptance/rpc_batch1.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,31 @@ describe('@api-batch-1 RPC Server Acceptance Tests', function () {
267267
}
268268
});
269269

270+
it('should be able to use `address` param with a large block range', async () => {
271+
const blockRangeLimit = constants.DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT;
272+
constants.DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT = 10;
273+
try {
274+
//when we pass only address, it defaults to the latest block
275+
const logs = await relay.call(
276+
RelayCalls.ETH_ENDPOINTS.ETH_GET_LOGS,
277+
[
278+
{
279+
fromBlock: numberTo0x(latestBlock - constants.DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT - 1),
280+
address: contractAddress,
281+
},
282+
],
283+
requestId,
284+
);
285+
expect(logs.length).to.be.greaterThan(0);
286+
287+
for (const i in logs) {
288+
expect(logs[i].address).to.equal(contractAddress);
289+
}
290+
} finally {
291+
constants.DEFAULT_ETH_GET_LOGS_BLOCK_RANGE_LIMIT = blockRangeLimit;
292+
}
293+
});
294+
270295
it('should be able to use `address` param with multiple addresses', async () => {
271296
const logs = await relay.call(
272297
RelayCalls.ETH_ENDPOINTS.ETH_GET_LOGS,

0 commit comments

Comments
 (0)