diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogs.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogs.java index 3b96dc38756..ec68ced39ed 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogs.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogs.java @@ -46,10 +46,7 @@ public JsonRpcResponse response(final JsonRpcRequest request) { final FilterParameter filter = parameters.required(request.getParams(), 0, FilterParameter.class); final LogsQuery query = - new LogsQuery.Builder() - .addresses(filter.getAddresses()) - .topics(filter.getTopics().getTopics()) - .build(); + new LogsQuery.Builder().addresses(filter.getAddresses()).topics(filter.getTopics()).build(); if (isValid(filter)) { return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameter.java index 31dbd495ecd..2275ef53534 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameter.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameter.java @@ -14,12 +14,12 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import static java.util.Collections.emptyList; + import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.LogTopic; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import com.fasterxml.jackson.annotation.JsonCreator; @@ -33,7 +33,7 @@ public class FilterParameter { private final BlockParameter fromBlock; private final BlockParameter toBlock; private final List
addresses; - private final TopicsParameter topics; + private final List> topics; private final Hash blockhash; @JsonCreator @@ -41,26 +41,18 @@ public FilterParameter( @JsonProperty("fromBlock") final String fromBlock, @JsonProperty("toBlock") final String toBlock, @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address") - final List address, + final List
address, @JsonDeserialize(using = TopicsDeserializer.class) @JsonProperty("topics") - final TopicsParameter topics, + final List> topics, @JsonProperty("blockhash") final String blockhash) { this.fromBlock = fromBlock != null ? new BlockParameter(fromBlock) : new BlockParameter("latest"); this.toBlock = toBlock != null ? new BlockParameter(toBlock) : new BlockParameter("latest"); - this.addresses = address != null ? renderAddress(address) : Collections.emptyList(); - this.topics = topics != null ? topics : new TopicsParameter(Collections.emptyList()); + this.addresses = address != null ? address : emptyList(); + this.topics = topics != null ? topics : emptyList(); this.blockhash = blockhash != null ? Hash.fromHexString(blockhash) : null; } - private List
renderAddress(final List inputAddresses) { - final List
addresses = new ArrayList<>(); - for (final String value : inputAddresses) { - addresses.add(Address.fromHexString(value)); - } - return addresses; - } - public BlockParameter getFromBlock() { return fromBlock; } @@ -73,7 +65,7 @@ public List
getAddresses() { return addresses; } - public TopicsParameter getTopics() { + public List> getTopics() { return topics; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TopicsDeserializer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TopicsDeserializer.java index 45c8a6d95e8..11ab23f7244 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TopicsDeserializer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TopicsDeserializer.java @@ -14,10 +14,11 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import static java.util.Collections.singletonList; + +import org.hyperledger.besu.ethereum.core.LogTopic; import java.io.IOException; -import java.util.Collections; import java.util.List; import com.fasterxml.jackson.core.JsonParser; @@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.google.common.collect.Lists; -public class TopicsDeserializer extends StdDeserializer { +public class TopicsDeserializer extends StdDeserializer>> { public TopicsDeserializer() { this(null); } @@ -36,27 +37,27 @@ public TopicsDeserializer(final Class vc) { } @Override - public TopicsParameter deserialize( + public List> deserialize( final JsonParser jsonparser, final DeserializationContext context) throws IOException { final JsonNode topicsNode = jsonparser.getCodec().readTree(jsonparser); - final List> topics = Lists.newArrayList(); + final List> topics = Lists.newArrayList(); if (!topicsNode.isArray()) { - topics.add(Collections.singletonList(topicsNode.textValue())); + topics.add(singletonList(LogTopic.fromHexString(topicsNode.textValue()))); } else { for (JsonNode child : topicsNode) { if (child.isArray()) { - final List childItems = Lists.newArrayList(); + final List childItems = Lists.newArrayList(); for (JsonNode subChild : child) { - childItems.add(subChild.textValue()); + childItems.add(LogTopic.fromHexString(subChild.textValue())); } topics.add(childItems); } else { - topics.add(Collections.singletonList(child.textValue())); + topics.add(singletonList(LogTopic.fromHexString(child.textValue()))); } } } - return new TopicsParameter(topics); + return topics; } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilder.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilder.java index 31d0144c929..0402c1f46fc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilder.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilder.java @@ -39,7 +39,7 @@ public Subscription build( return new LogsSubscription( subscriptionId, connectionId, - Optional.ofNullable(request.getFilterParameter()) + Optional.ofNullable(request.getLogsQuery()) .orElseThrow(IllegalArgumentException::new)); } case SYNCING: diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscription.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscription.java index 08fb5be01a9..d9e125f2f98 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscription.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscription.java @@ -14,25 +14,21 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.logs; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.Subscription; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType; import org.hyperledger.besu.ethereum.api.query.LogsQuery; public class LogsSubscription extends Subscription { - private final FilterParameter filterParameter; + private final LogsQuery logsQuery; public LogsSubscription( - final Long subscriptionId, final String connectionId, final FilterParameter filterParameter) { + final Long subscriptionId, final String connectionId, final LogsQuery logsQuery) { super(subscriptionId, connectionId, SubscriptionType.LOGS, Boolean.FALSE); - this.filterParameter = filterParameter; + this.logsQuery = logsQuery; } public LogsQuery getLogsQuery() { - return new LogsQuery.Builder() - .addresses(filterParameter.getAddresses()) - .topics(filterParameter.getTopics()) - .build(); + return logsQuery; } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/LogsSubscriptionParam.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/LogsSubscriptionParam.java deleted file mode 100644 index fbe495b23a5..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/LogsSubscriptionParam.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright ConsenSys AG. - * - * 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.websocket.subscription.request; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; - -class LogsSubscriptionParam { - - private final List address; - private final List topics; - - @JsonCreator - LogsSubscriptionParam( - @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address") - final List address, - @JsonProperty("topics") final List topics) { - this.address = address; - this.topics = topics; - } - - List address() { - return address; - } - - List topics() { - return topics; - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscribeRequest.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscribeRequest.java index 22ea539ee64..0775d9f3b53 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscribeRequest.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscribeRequest.java @@ -14,7 +14,7 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; +import org.hyperledger.besu.ethereum.api.query.LogsQuery; import java.util.Objects; @@ -22,17 +22,17 @@ public class SubscribeRequest { private final SubscriptionType subscriptionType; private final Boolean includeTransaction; - private final FilterParameter filterParameter; + private final LogsQuery logsQuery; private final String connectionId; public SubscribeRequest( final SubscriptionType subscriptionType, - final FilterParameter filterParameter, + final LogsQuery logsQuery, final Boolean includeTransaction, final String connectionId) { this.subscriptionType = subscriptionType; this.includeTransaction = includeTransaction; - this.filterParameter = filterParameter; + this.logsQuery = logsQuery; this.connectionId = connectionId; } @@ -40,8 +40,8 @@ public SubscriptionType getSubscriptionType() { return subscriptionType; } - public FilterParameter getFilterParameter() { - return filterParameter; + public LogsQuery getLogsQuery() { + return logsQuery; } public Boolean getIncludeTransaction() { @@ -54,16 +54,9 @@ public String getConnectionId() { @Override public String toString() { - return "SubscribeRequest{" - + "subscriptionType=" - + subscriptionType - + ", includeTransaction=" - + includeTransaction - + ", filterParameter=" - + filterParameter - + ", connectionId=" - + connectionId - + '}'; + return String.format( + "SubscribeRequest{subscriptionType=%s, includeTransaction=%s, logsQuery=%s, connectionId=%s}", + subscriptionType, includeTransaction, logsQuery, connectionId); } @Override @@ -77,12 +70,12 @@ public boolean equals(final Object o) { final SubscribeRequest that = (SubscribeRequest) o; return subscriptionType == that.subscriptionType && Objects.equals(includeTransaction, that.includeTransaction) - && Objects.equals(filterParameter, that.filterParameter) + && Objects.equals(logsQuery, that.logsQuery) && Objects.equals(connectionId, that.connectionId); } @Override public int hashCode() { - return Objects.hash(subscriptionType, includeTransaction, filterParameter, connectionId); + return Objects.hash(subscriptionType, includeTransaction, logsQuery, connectionId); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapper.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapper.java index 13153b5dc83..2b8ff7301e4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapper.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapper.java @@ -15,15 +15,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.UnsignedLongParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketRpcRequest; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import org.hyperledger.besu.ethereum.api.query.LogsQuery; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Optional; public class SubscriptionRequestMapper { @@ -77,31 +73,8 @@ private SubscribeRequest parseNewBlockHeadersRequest( private SubscribeRequest parseLogsRequest( final WebSocketRpcRequest request, final JsonRpcParameter parameter) { - final LogsSubscriptionParam logFilterParams = - parameter.required(request.getParams(), 1, LogsSubscriptionParam.class); - return new SubscribeRequest( - SubscriptionType.LOGS, - createFilterParameter(logFilterParams), - null, - request.getConnectionId()); - } - - private FilterParameter createFilterParameter(final LogsSubscriptionParam logFilterParams) { - final List addresses = hasAddresses(logFilterParams); - final List> topics = hasTopics(logFilterParams); - return new FilterParameter(null, null, addresses, new TopicsParameter(topics), null); - } - - private List hasAddresses(final LogsSubscriptionParam logFilterParams) { - return logFilterParams.address() != null && !logFilterParams.address().isEmpty() - ? logFilterParams.address() - : Collections.emptyList(); - } - - private List> hasTopics(final LogsSubscriptionParam logFilterParams) { - return logFilterParams.topics() != null && !logFilterParams.topics().isEmpty() - ? Arrays.asList(logFilterParams.topics()) - : Collections.emptyList(); + final LogsQuery logsQuery = parameter.required(request.getParams(), 1, LogsQuery.class); + return new SubscribeRequest(SubscriptionType.LOGS, logsQuery, null, request.getConnectionId()); } public UnsubscribeRequest mapUnsubscribeRequest(final JsonRpcRequest jsonRpcRequest) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/LogsQuery.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/LogsQuery.java index 3157ba9be82..2cc76a8858c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/LogsQuery.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/LogsQuery.java @@ -16,6 +16,10 @@ */ package org.hyperledger.besu.ethereum.api.query; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toUnmodifiableList; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.TopicsDeserializer; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.LogTopic; @@ -25,32 +29,40 @@ import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.google.common.collect.Lists; public class LogsQuery { - private final List
queryAddresses; - private final List> queryTopics; + private final List
addresses; + private final List> topics; private final List addressBlooms; private final List> topicsBlooms; - private LogsQuery(final List
queryAddresses, final List> queryTopics) { - this.queryAddresses = queryAddresses; - this.queryTopics = queryTopics; - addressBlooms = - this.queryAddresses.stream() - .map(LogsBloomFilter::computeBytes) - .collect(Collectors.toList()); - topicsBlooms = - this.queryTopics.stream() + @JsonCreator + public LogsQuery( + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address") + final List
addresses, + @JsonDeserialize(using = TopicsDeserializer.class) @JsonProperty("topics") + final List> topics) { + this.addresses = addresses != null ? addresses : emptyList(); + this.topics = topics != null ? topics : emptyList(); + this.addressBlooms = + this.addresses.stream().map(LogsBloomFilter::computeBytes).collect(toUnmodifiableList()); + this.topicsBlooms = + this.topics.stream() .map( - topics -> - topics.stream() + subTopics -> + subTopics.stream() .filter(Objects::nonNull) .map(LogsBloomFilter::computeBytes) .collect(Collectors.toList())) - .collect(Collectors.toList()); + .collect(toUnmodifiableList()); } public boolean couldMatch(final LogsBloomFilter bloom) { @@ -66,22 +78,14 @@ public boolean matches(final Log log) { } private boolean matchesAddresses(final Address address) { - return queryAddresses.isEmpty() || queryAddresses.contains(address); + return addresses.isEmpty() || addresses.contains(address); } private boolean matchesTopics(final List topics) { - if (queryTopics.isEmpty()) { - return true; - } - if (topics.size() < queryTopics.size()) { - return false; - } - for (int i = 0; i < queryTopics.size(); ++i) { - if (!matchesTopic(topics.get(i), queryTopics.get(i))) { - return false; - } - } - return true; + return this.topics.isEmpty() + || (topics.size() >= this.topics.size() + && IntStream.range(0, this.topics.size()) + .allMatch(i -> matchesTopic(topics.get(i), this.topics.get(i)))); } private boolean matchesTopic(final LogTopic topic, final List matchCriteria) { @@ -93,13 +97,19 @@ public boolean equals(final Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final LogsQuery logsQuery = (LogsQuery) o; - return Objects.equals(queryAddresses, logsQuery.queryAddresses) - && Objects.equals(queryTopics, logsQuery.queryTopics); + return Objects.equals(addresses, logsQuery.addresses) + && Objects.equals(topics, logsQuery.topics); + } + + @Override + public String toString() { + return String.format( + "%s{addresses=%s, topics=%s", getClass().getSimpleName(), addresses, topics); } @Override public int hashCode() { - return Objects.hash(queryAddresses, queryTopics); + return Objects.hash(addresses, topics); } public static class Builder { @@ -134,13 +144,6 @@ public Builder topics(final List> topics) { return this; } - public Builder topics(final TopicsParameter topicsParameter) { - if (topicsParameter != null) { - topics(topicsParameter.getTopics()); - } - return this; - } - public LogsQuery build() { return new LogsQuery(queryAddresses, queryTopics); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TopicsParameter.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TopicsParameter.java deleted file mode 100644 index 1aaf3088fdc..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TopicsParameter.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright ConsenSys AG. - * - * 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.query; - -import org.hyperledger.besu.ethereum.core.LogTopic; -import org.hyperledger.besu.util.bytes.BytesValue; - -import java.util.ArrayList; -import java.util.List; - -public class TopicsParameter { - - private final List> queryTopics = new ArrayList<>(); - - public TopicsParameter(final List> topics) { - if (topics != null) { - for (final List list : topics) { - final List inputTopics = new ArrayList<>(); - if (list != null) { - for (final String input : list) { - final LogTopic topic = - input != null ? LogTopic.create(BytesValue.fromHexString(input)) : null; - inputTopics.add(topic); - } - } - queryTopics.add(inputTopics); - } - } - } - - public List> getTopics() { - return queryTopics; - } - - @Override - public String toString() { - return "TopicsParameter{" + "queryTopics=" + queryTopics + '}'; - } -} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthNewFilterTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthNewFilterTest.java index 7fd7a9472ad..a263b718fd1 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthNewFilterTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthNewFilterTest.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -29,11 +31,12 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.query.LogsQuery; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.LogTopic; -import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Optional; import org.junit.Before; import org.junit.Test; @@ -98,13 +101,12 @@ public void newFilterWithoutAddressAndTopicsParamsInstallsEmptyLogFilter() { @Test public void newFilterWithTopicsOnlyParamInstallsExpectedLogFilter() { - final List> topics = topics(); - final FilterParameter filterParameter = filterParamWithAddressAndTopics(null, topics); + final FilterParameter filterParameter = filterParamWithAddressAndTopics(null, topics()); final JsonRpcRequest request = ethNewFilter(filterParameter); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1"); final LogsQuery expectedLogsQuery = - new LogsQuery.Builder().topics(new TopicsParameter(topics)).build(); + new LogsQuery.Builder().topics(filterParameter.getTopics()).build(); when(filterManager.installLogFilter(any(), any(), eq(expectedLogsQuery))).thenReturn("0x1"); final JsonRpcResponse actualResponse = method.response(request); @@ -136,13 +138,13 @@ public void newFilterWithAddressOnlyParamInstallsExpectedLogFilter() { @Test public void newFilterWithAddressAndTopicsParamInstallsExpectedLogFilter() { final Address address = Address.fromHexString("0x0"); - final List> topics = topics(); + final List> topics = topics(); final FilterParameter filterParameter = filterParamWithAddressAndTopics(address, topics); final JsonRpcRequest request = ethNewFilter(filterParameter); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1"); final LogsQuery expectedLogsQuery = - new LogsQuery.Builder().address(address).topics(new TopicsParameter(topics)).build(); + new LogsQuery.Builder().address(address).topics(filterParameter.getTopics()).build(); when(filterManager.installLogFilter(any(), any(), eq(expectedLogsQuery))).thenReturn("0x1"); final JsonRpcResponse actualResponse = method.response(request); @@ -153,15 +155,21 @@ public void newFilterWithAddressAndTopicsParamInstallsExpectedLogFilter() { refEq(blockParamLatest()), refEq(blockParamLatest()), eq(expectedLogsQuery)); } - private List> topics() { - return Arrays.asList( - Arrays.asList("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")); + private List> topics() { + return singletonList( + singletonList( + LogTopic.fromHexString( + "0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"))); } private FilterParameter filterParamWithAddressAndTopics( - final Address address, final List> topics) { - final List addresses = address != null ? Arrays.asList(address.toString()) : null; - return new FilterParameter("latest", "latest", addresses, new TopicsParameter(topics), null); + final Address address, final List> topics) { + return new FilterParameter( + "latest", + "latest", + Optional.ofNullable(address).map(Collections::singletonList).orElse(emptyList()), + topics, + null); } private JsonRpcRequest ethNewFilter(final FilterParameter filterParameter) { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameterTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameterTest.java index c5938fc18e1..8bc00a89ed3 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameterTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameterTest.java @@ -16,10 +16,11 @@ import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toUnmodifiableList; import static org.assertj.core.api.Assertions.assertThat; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.LogTopic; import java.util.Arrays; @@ -142,7 +143,7 @@ public void whenTopicsParameterIsArrayOfStringsFilterContainsListOfSingletonList "0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74"); final FilterParameter filter = createFilterWithTopics(topics); - assertThat(filter.getTopics().getTopics()) + assertThat(filter.getTopics()) .containsExactly( singletonList( LogTopic.fromHexString( @@ -164,7 +165,7 @@ public void whenTopicsParameterContainsArraysFilterContainsListOfSingletonLists( final FilterParameter filter = createFilterWithTopics(topics); - assertThat(filter.getTopics().getTopics()) + assertThat(filter.getTopics()) .containsExactly( singletonList( LogTopic.fromHexString( @@ -185,7 +186,7 @@ public void whenTopicArrayContainsNullFilterContainsSingletonListOfAllTopics() singletonList("0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74")); final FilterParameter filter = createFilterWithTopics(topics); - assertThat(filter.getTopics().getTopics()) + assertThat(filter.getTopics()) .containsExactly( singletonList( LogTopic.fromHexString( @@ -201,7 +202,7 @@ public void emptyListDecodesCorrectly() throws JsonProcessingException { final List topics = emptyList(); final FilterParameter filter = createFilterWithTopics(topics); - assertThat(filter.getTopics().getTopics().size()).isZero(); + assertThat(filter.getTopics().size()).isZero(); } @Test @@ -211,7 +212,7 @@ public void emptyListAsSubTopicDecodesCorrectly() throws JsonProcessingException singletonList("0xce8688f853ffa65c042b72302433c25d7a230c322caba0901587534b6551091d"), emptyList()); final FilterParameter filter = createFilterWithTopics(topics); - assertThat(filter.getTopics().getTopics()) + assertThat(filter.getTopics()) .containsExactly( singletonList( LogTopic.fromHexString( @@ -229,7 +230,12 @@ private FilterParameter createFilterWithTopics(final T inputTopics) } private FilterParameter filterParameterWithAddresses(final String... addresses) { - return new FilterParameter("latest", "latest", Arrays.asList(addresses), null, null); + return new FilterParameter( + "latest", + "latest", + Arrays.stream(addresses).map(Address::fromHexString).collect(toUnmodifiableList()), + null, + null); } private FilterParameter filterParameterWithAddressAndSingleListOfTopics( @@ -237,17 +243,19 @@ private FilterParameter filterParameterWithAddressAndSingleListOfTopics( return new FilterParameter( "latest", "latest", - Arrays.asList(address), - new TopicsParameter(singletonList(Arrays.asList(topics))), + singletonList(Address.fromHexString(address)), + singletonList( + Arrays.stream(topics).map(LogTopic::fromHexString).collect(toUnmodifiableList())), null); } private FilterParameter filterParameterWithAddressAndMultipleListOfTopics( final String address, final String... topics) { - List topicsList = Arrays.asList(topics); - List> topicsListList = Arrays.asList(topicsList, topicsList); + List topicsList = + Arrays.stream(topics).map(LogTopic::fromHexString).collect(toUnmodifiableList()); + List> topicsListList = Arrays.asList(topicsList, topicsList); return new FilterParameter( - "latest", "latest", Arrays.asList(address), new TopicsParameter(topicsListList), null); + "latest", "latest", singletonList(Address.fromHexString(address)), topicsListList, null); } private JsonRpcRequest readJsonAsJsonRpcRequest(final String jsonWithSingleAddress) diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilderTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilderTest.java index c2fb16fa91c..ac846fade60 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilderTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilderTest.java @@ -17,12 +17,12 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.blockheaders.NewBlockHeadersSubscription; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.logs.LogsSubscription; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscribeRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.syncing.SyncingSubscription; +import org.hyperledger.besu.ethereum.api.query.LogsQuery; import java.util.function.Function; @@ -41,11 +41,11 @@ public void before() { @Test public void shouldBuildLogsSubscriptionWhenSubscribeRequestTypeIsLogs() { - final FilterParameter filterParameter = filterParameter(); + final LogsQuery logsQuery = logsQuery(); final SubscribeRequest subscribeRequest = - new SubscribeRequest(SubscriptionType.LOGS, filterParameter, null, CONNECTION_ID); + new SubscribeRequest(SubscriptionType.LOGS, logsQuery, null, CONNECTION_ID); final LogsSubscription expectedSubscription = - new LogsSubscription(1L, CONNECTION_ID, filterParameter); + new LogsSubscription(1L, CONNECTION_ID, logsQuery); final Subscription builtSubscription = subscriptionBuilder.build(1L, CONNECTION_ID, subscribeRequest); @@ -96,7 +96,7 @@ public void shouldBuildSubscriptionWhenSubscribeRequestTypeIsSyncing() { public void shouldReturnLogsSubscriptionWhenMappingLogsSubscription() { final Function function = subscriptionBuilder.mapToSubscriptionClass(LogsSubscription.class); - final Subscription subscription = new LogsSubscription(1L, CONNECTION_ID, filterParameter()); + final Subscription subscription = new LogsSubscription(1L, CONNECTION_ID, logsQuery()); assertThat(function.apply(subscription)).isInstanceOf(LogsSubscription.class); } @@ -147,7 +147,7 @@ public void shouldThrownIllegalArgumentExceptionWhenMappingWrongSubscriptionType "NewBlockHeadersSubscription instance can't be mapped to type LogsSubscription"); } - private FilterParameter filterParameter() { - return new FilterParameter(null, null, null, null, null); + private LogsQuery logsQuery() { + return new LogsQuery(null, null); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscriptionServiceTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscriptionServiceTest.java index 3bb25154e7d..25503ba2dde 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscriptionServiceTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscriptionServiceTest.java @@ -21,11 +21,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.LogResult; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import org.hyperledger.besu.ethereum.api.query.LogsQuery; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Block; @@ -352,19 +351,9 @@ private LogsSubscription createSubscription(final Address address) { private LogsSubscription createSubscription( final List
addresses, final List> logTopics) { - // TODO: FilterParameter constructor should work with proper types instead of Strings - final List addressStrings = - addresses.stream().map(Address::toString).collect(Collectors.toList()); - final List> topicStrings = - logTopics.stream() - .map(topics -> topics.stream().map(LogTopic::toString).collect(Collectors.toList())) - .collect(Collectors.toList()); - final FilterParameter filterParameter = - new FilterParameter(null, null, addressStrings, new TopicsParameter(topicStrings), null); - final LogsSubscription logsSubscription = - new LogsSubscription(nextSubscriptionId.incrementAndGet(), "conn", filterParameter); - return logsSubscription; + return new LogsSubscription( + nextSubscriptionId.incrementAndGet(), "conn", new LogsQuery(addresses, logTopics)); } private void registerSubscriptions(final LogsSubscription... subscriptions) { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapperTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapperTest.java index e03ec6ce005..a56b5d47385 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapperTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapperTest.java @@ -14,6 +14,9 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toUnmodifiableList; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.both; import static org.hamcrest.CoreMatchers.equalTo; @@ -22,13 +25,14 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.FilterParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketRpcRequest; -import org.hyperledger.besu.ethereum.api.query.TopicsParameter; +import org.hyperledger.besu.ethereum.api.query.LogsQuery; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.LogTopic; -import java.util.Arrays; -import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; import io.vertx.core.json.Json; import org.junit.Before; @@ -158,15 +162,14 @@ public void mapRequestWithSingleAddress() { parseWebSocketRpcRequest( "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}"); - final FilterParameter expectedFilterParam = - new FilterParameter( - null, + final SubscribeRequest expectedSubscribeRequest = + new SubscribeRequest( + SubscriptionType.LOGS, + new LogsQuery( + singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")), + emptyList()), null, - Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"), - new TopicsParameter(Collections.emptyList()), null); - final SubscribeRequest expectedSubscribeRequest = - new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); @@ -180,20 +183,21 @@ public void mapRequestWithMultipleAddresses() { parseWebSocketRpcRequest( "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": [\"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"0xf17f52151EbEF6C7334FAD080c5704D77216b732\"], \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\"]}]}"); - final FilterParameter expectedFilterParam = - new FilterParameter( - null, + final SubscribeRequest expectedSubscribeRequest = + new SubscribeRequest( + SubscriptionType.LOGS, + new LogsQuery( + Stream.of( + "0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", + "0xf17f52151EbEF6C7334FAD080c5704D77216b732") + .map(Address::fromHexString) + .collect(toUnmodifiableList()), + singletonList( + singletonList( + LogTopic.fromHexString( + "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902")))), null, - Arrays.asList( - "0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", - "0xf17f52151EbEF6C7334FAD080c5704D77216b732"), - new TopicsParameter( - Arrays.asList( - Arrays.asList( - "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"))), null); - final SubscribeRequest expectedSubscribeRequest = - new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); @@ -207,19 +211,20 @@ public void mapRequestWithMultipleTopics() { parseWebSocketRpcRequest( "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\", \"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901\"]}]}"); - final FilterParameter expectedFilterParam = - new FilterParameter( - null, + final SubscribeRequest expectedSubscribeRequest = + new SubscribeRequest( + SubscriptionType.LOGS, + new LogsQuery( + singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")), + List.of( + singletonList( + LogTopic.fromHexString( + "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902")), + singletonList( + LogTopic.fromHexString( + "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901")))), null, - Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"), - new TopicsParameter( - Arrays.asList( - Arrays.asList( - "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902", - "0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901"))), null); - final SubscribeRequest expectedSubscribeRequest = - new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); @@ -233,15 +238,14 @@ public void mapRequestToLogsWithoutTopics() { parseWebSocketRpcRequest( "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}"); - final FilterParameter expectedFilterParam = - new FilterParameter( - null, + final SubscribeRequest expectedSubscribeRequest = + new SubscribeRequest( + SubscriptionType.LOGS, + new LogsQuery( + singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")), + emptyList()), null, - Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"), - new TopicsParameter(Collections.emptyList()), null); - final SubscribeRequest expectedSubscribeRequest = - new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); @@ -257,8 +261,8 @@ public void mapRequestToLogsWithInvalidTopicInFilter() { thrown.expect(InvalidSubscriptionRequestException.class); thrown.expectCause( - both(hasMessage(equalTo("Invalid odd-length hex binary representation 0x1"))) - .and(instanceOf(IllegalArgumentException.class))); + both(hasMessage(equalTo("Invalid json rpc parameter at index 1"))) + .and(instanceOf(InvalidJsonRpcParameters.class))); mapper.mapSubscribeRequest(jsonRpcRequest); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/LogTopic.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/LogTopic.java index 3ffa370c2fd..0eb1579f353 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/LogTopic.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/LogTopic.java @@ -44,7 +44,7 @@ public static LogTopic of(final BytesValue bytes) { } public static LogTopic fromHexString(final String str) { - return new LogTopic(BytesValue.fromHexString(str)); + return str == null ? null : LogTopic.create(BytesValue.fromHexString(str)); } /**