diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogsTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogsTest.java new file mode 100644 index 00000000000..e2fc68c7c23 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogsTest.java @@ -0,0 +1,264 @@ +/* + * Copyright contributors to Hyperledger Besu + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.BlockHeader; + +import java.util.ArrayList; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class EthGetLogsTest { + + private EthGetLogs method; + + @Mock BlockchainQueries blockchainQueries; + + @Before + public void setUp() { + method = new EthGetLogs(blockchainQueries); + } + + @Test + public void shouldReturnCorrectMethodName() { + assertThat(method.getName()).isEqualTo("eth_getLogs"); + } + + @Test + public void shouldReturnErrorWhenMissingParams() { + final JsonRpcRequestContext request = + new JsonRpcRequestContext(new JsonRpcRequest("2.0", "eth_getLogs", new Object[] {})); + + final Throwable thrown = catchThrowable(() -> method.response(request)); + assertThat(thrown) + .isInstanceOf(InvalidJsonRpcParameters.class) + .hasMessage("Missing required json rpc parameter at index 0"); + + verifyNoInteractions(blockchainQueries); + } + + @Test + public void shouldQueryFinalizedBlock() { + final long blockNumber = 7L; + final JsonRpcRequestContext request = buildRequest("finalized", "finalized"); + + final BlockHeader blockHeader = mock(BlockHeader.class); + + when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(blockHeader)); + when(blockHeader.getNumber()).thenReturn(blockNumber); + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(blockNumber), eq(blockNumber), any(), any()); + verify(blockchainQueries, never()).headBlockNumber(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQueryLatestBlock() { + final long latestBlockNumber = 7L; + final JsonRpcRequestContext request = buildRequest("latest", "latest"); + + when(blockchainQueries.headBlockNumber()).thenReturn(latestBlockNumber); + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries) + .matchingLogs(eq(latestBlockNumber), eq(latestBlockNumber), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQuerySafeBlock() { + final long blockNumber = 7L; + final JsonRpcRequestContext request = buildRequest("safe", "safe"); + + final BlockHeader blockHeader = mock(BlockHeader.class); + + when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(blockHeader)); + when(blockHeader.getNumber()).thenReturn(blockNumber); + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(blockNumber), eq(blockNumber), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).headBlockNumber(); + } + + @Test + public void shouldQueryNumericBlock() { + final long fromBlock = 3L; + final long toBlock = 10L; + final JsonRpcRequestContext request = buildRequest(fromBlock, toBlock); + + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(fromBlock), eq(toBlock), any(), any()); + + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).headBlockNumber(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQueryEarliestToNumeric() { + final long genesisBlock = 0L; + final long latestBlock = 50L; + final JsonRpcRequestContext request = buildRequest("earliest", latestBlock); + + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(genesisBlock), eq(latestBlock), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQueryWrappedNumeric() { + final long fromBlock = 50L; + final long toBlock = 100L; + final JsonRpcRequestContext request = + buildRequest(String.valueOf(fromBlock), String.valueOf(toBlock)); + + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(fromBlock), eq(toBlock), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).headBlockNumber(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQueryWrappedNumericToNumeric() { + final long fromBlock = 50L; + final long toBlock = 100L; + final JsonRpcRequestContext request = buildRequest(String.valueOf(fromBlock), toBlock); + + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(fromBlock), eq(toBlock), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).headBlockNumber(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQueryEarliestToLatest() { + final long genesisBlock = 0L; + final long latestBlock = 50L; + final JsonRpcRequestContext request = buildRequest("earliest", "latest"); + + when(blockchainQueries.headBlockNumber()).thenReturn(latestBlock); + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries).matchingLogs(eq(genesisBlock), eq(latestBlock), any(), any()); + verify(blockchainQueries, never()).finalizedBlockHeader(); + verify(blockchainQueries, never()).safeBlockHeader(); + } + + @Test + public void shouldQuerySafeToFinalized() { + final long finalizedBlockNumber = 50L; + final long safeBlockNumber = 25L; + final JsonRpcRequestContext request = buildRequest("safe", "Finalized"); + + final BlockHeader finalizedBlockHeader = mock(BlockHeader.class); + final BlockHeader safeBlockHeader = mock(BlockHeader.class); + + when(blockchainQueries.finalizedBlockHeader()).thenReturn(Optional.of(finalizedBlockHeader)); + when(blockchainQueries.safeBlockHeader()).thenReturn(Optional.of(safeBlockHeader)); + when(finalizedBlockHeader.getNumber()).thenReturn(finalizedBlockNumber); + when(safeBlockHeader.getNumber()).thenReturn(safeBlockNumber); + when(blockchainQueries.matchingLogs(anyLong(), anyLong(), any(), any())) + .thenReturn(new ArrayList<>()); + + method.response(request); + + verify(blockchainQueries) + .matchingLogs(eq(safeBlockNumber), eq(finalizedBlockNumber), any(), any()); + verify(blockchainQueries, never()).headBlockNumber(); + } + + private JsonRpcRequestContext buildRequest(final long fromBlock, final long toBlock) { + final FilterParameter filterParameter = + buildFilterParameter(new BlockParameter(fromBlock), new BlockParameter(toBlock)); + return new JsonRpcRequestContext( + new JsonRpcRequest("2.0", "eth_getLogs", new Object[] {filterParameter})); + } + + private JsonRpcRequestContext buildRequest(final String fromBlock, final long toBlock) { + final FilterParameter filterParameter = + buildFilterParameter(new BlockParameter(fromBlock), new BlockParameter(toBlock)); + return new JsonRpcRequestContext( + new JsonRpcRequest("2.0", "eth_getLogs", new Object[] {filterParameter})); + } + + private JsonRpcRequestContext buildRequest(final String fromBlock, final String toBlock) { + final FilterParameter filterParameter = + buildFilterParameter(new BlockParameter(fromBlock), new BlockParameter(toBlock)); + return new JsonRpcRequestContext( + new JsonRpcRequest("2.0", "eth_getLogs", new Object[] {filterParameter})); + } + + private FilterParameter buildFilterParameter( + final BlockParameter fromBlock, final BlockParameter toBlock) { + return new FilterParameter(fromBlock, toBlock, null, null, null, null, null, null, null); + } +}