diff --git a/packages/relay/src/lib/services/ethService/ethCommonService/index.ts b/packages/relay/src/lib/services/ethService/ethCommonService/index.ts index 4279c967ea..da37065176 100644 --- a/packages/relay/src/lib/services/ethService/ethCommonService/index.ts +++ b/packages/relay/src/lib/services/ethService/ethCommonService/index.ts @@ -346,13 +346,28 @@ export class CommonService implements ICommonService { const logs: Log[] = []; for (const log of logResults) { + if (log.block_number == null || log.index == null || log.block_hash === EthImpl.emptyHex) { + if (this.logger.isLevelEnabled('debug')) { + this.logger.debug( + `${ + requestDetails.formattedRequestId + } Log entry is missing required fields: block_number, index, or block_hash is an empty hex (0x). log=${JSON.stringify( + log, + )}`, + ); + } + throw predefined.INTERNAL_ERROR( + `The log entry from the remote Mirror Node server is missing required fields. `, + ); + } + logs.push( new Log({ address: log.address, blockHash: toHash32(log.block_hash), - blockNumber: nullableNumberTo0x(log.block_number), + blockNumber: numberTo0x(log.block_number), data: log.data, - logIndex: nullableNumberTo0x(log.index), + logIndex: numberTo0x(log.index), removed: false, topics: log.topics, transactionHash: toHash32(log.transaction_hash), diff --git a/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts index 2696d5b717..22573e6baf 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockByHash.spec.ts @@ -367,28 +367,34 @@ describe('@ethGetBlockByHash using MirrorNode', async function () { }); }); - it('eth_getBlockByHash should gracefully handle nulbale entities found in logs', async function () { + it('eth_getBlockByHash should throw an error if nulbale entities found in logs', async function () { // mirror node request mocks restMock.onGet(`blocks/${BLOCK_HASH}`).reply(200, DEFAULT_BLOCK); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); - const nullEntitiedLogs = { - logs: [{ ...DEFAULT_LOGS.logs[0], block_number: null, transaction_index: null, logIndex: null }], - }; - - restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, nullEntitiedLogs); + const nullEntitiedLogs = [ + { + logs: [{ ...DEFAULT_LOGS.logs[0], block_number: null }], + }, + { + logs: [{ ...DEFAULT_LOGS.logs[0], index: null }], + }, + { + logs: [{ ...DEFAULT_LOGS.logs[0], block_hash: '0x' }], + }, + ]; - const result = await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); + for (const logEntry of nullEntitiedLogs) { + try { + restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, logEntry); - RelayAssertions.assertBlock(result, { - hash: BLOCK_HASH_TRIMMED, - gasUsed: TOTAL_GAS_USED, - number: BLOCK_NUMBER_HEX, - parentHash: BLOCK_HASH_PREV_TRIMMED, - timestamp: BLOCK_TIMESTAMP_HEX, - transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2], - receiptsRoot: DEFAULT_BLOCK_RECEIPTS_ROOT_HASH, - }); + await ethImpl.getBlockByHash(BLOCK_HASH, false, requestDetails); + expect.fail('should have thrown an error'); + } catch (error) { + expect(error).to.exist; + expect(error.message).to.include('The log entry from the remote Mirror Node server is missing required fields'); + } + } }); }); diff --git a/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts b/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts index 4242341ef2..84d0a44af5 100644 --- a/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts +++ b/packages/relay/tests/lib/eth/eth_getBlockByNumber.spec.ts @@ -612,28 +612,34 @@ describe('@ethGetBlockByNumber using MirrorNode', async function () { }); }); - it('eth_getBlockByNumber should gracefully handle nulbale entities found in logs', async function () { + it('eth_getBlockByNumber should throw an error if nulbale entities found in logs', async function () { // mirror node request mocks restMock.onGet(`blocks/${BLOCK_HASH}`).reply(200, DEFAULT_BLOCK); restMock.onGet(CONTRACT_RESULTS_WITH_FILTER_URL).reply(200, defaultContractResults); restMock.onGet('network/fees').reply(200, DEFAULT_NETWORK_FEES); - const nullEntitiedLogs = { - logs: [{ ...DEFAULT_LOGS.logs[0], block_number: null, transaction_index: null, logIndex: null }], - }; - - restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, nullEntitiedLogs); - - const result = await ethImpl.getBlockByNumber(BLOCK_HASH, false, requestDetails); - - RelayAssertions.assertBlock(result, { - hash: BLOCK_HASH_TRIMMED, - gasUsed: TOTAL_GAS_USED, - number: BLOCK_NUMBER_HEX, - parentHash: BLOCK_HASH_PREV_TRIMMED, - timestamp: BLOCK_TIMESTAMP_HEX, - transactions: [CONTRACT_HASH_1, CONTRACT_HASH_2], - receiptsRoot: DEFAULT_BLOCK_RECEIPTS_ROOT_HASH, - }); + const nullEntitiedLogs = [ + { + logs: [{ ...DEFAULT_LOGS.logs[0], block_number: null }], + }, + { + logs: [{ ...DEFAULT_LOGS.logs[0], index: null }], + }, + { + logs: [{ ...DEFAULT_LOGS.logs[0], block_hash: '0x' }], + }, + ]; + + for (const logEntry of nullEntitiedLogs) { + try { + restMock.onGet(CONTRACT_RESULTS_LOGS_WITH_FILTER_URL).reply(200, logEntry); + + await ethImpl.getBlockByNumber(BLOCK_HASH, false, requestDetails); + expect.fail('should have thrown an error'); + } catch (error) { + expect(error).to.exist; + expect(error.message).to.include('The log entry from the remote Mirror Node server is missing required fields'); + } + } }); });