Rework how filter and log query parameters are created/used (#146)

* Rework how filter and log query parameters are created/used

We used a `FilterParameter` that held strings in places where we could
create strongly typed objects. We also used it in places where we only
wanted a subset of its descriptiveness, namely, the `LogsQuery` part of
it.

* deserialize directly into `LogsQuery`, which is useful for log pub/sub
* narrow uses of `FilterParameter` to `LogsQuery` where possible
* make `FilterParameter` hold strongly typed `Address`s and `LogTopic`s

Signed-off-by: Ratan Rai Sur <ratan.r.sur@gmail.com>
pull/133/head
Ratan Rai Sur 5 years ago committed by GitHub
parent a07b450663
commit a48a2de7f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetLogs.java
  2. 26
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameter.java
  3. 21
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/TopicsDeserializer.java
  4. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilder.java
  5. 12
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscription.java
  6. 44
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/LogsSubscriptionParam.java
  7. 29
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscribeRequest.java
  8. 33
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapper.java
  9. 77
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/LogsQuery.java
  10. 53
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TopicsParameter.java
  11. 34
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthNewFilterTest.java
  12. 32
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/parameters/FilterParameterTest.java
  13. 14
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/SubscriptionBuilderTest.java
  14. 17
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/logs/LogsSubscriptionServiceTest.java
  15. 90
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/websocket/subscription/request/SubscriptionRequestMapperTest.java
  16. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/LogTopic.java

@ -46,10 +46,7 @@ public class EthGetLogs implements JsonRpcMethod {
final FilterParameter filter = final FilterParameter filter =
parameters.required(request.getParams(), 0, FilterParameter.class); parameters.required(request.getParams(), 0, FilterParameter.class);
final LogsQuery query = final LogsQuery query =
new LogsQuery.Builder() new LogsQuery.Builder().addresses(filter.getAddresses()).topics(filter.getTopics()).build();
.addresses(filter.getAddresses())
.topics(filter.getTopics().getTopics())
.build();
if (isValid(filter)) { if (isValid(filter)) {
return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS); return new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS);

@ -14,12 +14,12 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; 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.Address;
import org.hyperledger.besu.ethereum.core.Hash; 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 java.util.List;
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonCreator;
@ -33,7 +33,7 @@ public class FilterParameter {
private final BlockParameter fromBlock; private final BlockParameter fromBlock;
private final BlockParameter toBlock; private final BlockParameter toBlock;
private final List<Address> addresses; private final List<Address> addresses;
private final TopicsParameter topics; private final List<List<LogTopic>> topics;
private final Hash blockhash; private final Hash blockhash;
@JsonCreator @JsonCreator
@ -41,26 +41,18 @@ public class FilterParameter {
@JsonProperty("fromBlock") final String fromBlock, @JsonProperty("fromBlock") final String fromBlock,
@JsonProperty("toBlock") final String toBlock, @JsonProperty("toBlock") final String toBlock,
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address") @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address")
final List<String> address, final List<Address> address,
@JsonDeserialize(using = TopicsDeserializer.class) @JsonProperty("topics") @JsonDeserialize(using = TopicsDeserializer.class) @JsonProperty("topics")
final TopicsParameter topics, final List<List<LogTopic>> topics,
@JsonProperty("blockhash") final String blockhash) { @JsonProperty("blockhash") final String blockhash) {
this.fromBlock = this.fromBlock =
fromBlock != null ? new BlockParameter(fromBlock) : new BlockParameter("latest"); fromBlock != null ? new BlockParameter(fromBlock) : new BlockParameter("latest");
this.toBlock = toBlock != null ? new BlockParameter(toBlock) : new BlockParameter("latest"); this.toBlock = toBlock != null ? new BlockParameter(toBlock) : new BlockParameter("latest");
this.addresses = address != null ? renderAddress(address) : Collections.emptyList(); this.addresses = address != null ? address : emptyList();
this.topics = topics != null ? topics : new TopicsParameter(Collections.emptyList()); this.topics = topics != null ? topics : emptyList();
this.blockhash = blockhash != null ? Hash.fromHexString(blockhash) : null; this.blockhash = blockhash != null ? Hash.fromHexString(blockhash) : null;
} }
private List<Address> renderAddress(final List<String> inputAddresses) {
final List<Address> addresses = new ArrayList<>();
for (final String value : inputAddresses) {
addresses.add(Address.fromHexString(value));
}
return addresses;
}
public BlockParameter getFromBlock() { public BlockParameter getFromBlock() {
return fromBlock; return fromBlock;
} }
@ -73,7 +65,7 @@ public class FilterParameter {
return addresses; return addresses;
} }
public TopicsParameter getTopics() { public List<List<LogTopic>> getTopics() {
return topics; return topics;
} }

@ -14,10 +14,11 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters; 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.io.IOException;
import java.util.Collections;
import java.util.List; import java.util.List;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
@ -26,7 +27,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
public class TopicsDeserializer extends StdDeserializer<TopicsParameter> { public class TopicsDeserializer extends StdDeserializer<List<List<LogTopic>>> {
public TopicsDeserializer() { public TopicsDeserializer() {
this(null); this(null);
} }
@ -36,27 +37,27 @@ public class TopicsDeserializer extends StdDeserializer<TopicsParameter> {
} }
@Override @Override
public TopicsParameter deserialize( public List<List<LogTopic>> deserialize(
final JsonParser jsonparser, final DeserializationContext context) throws IOException { final JsonParser jsonparser, final DeserializationContext context) throws IOException {
final JsonNode topicsNode = jsonparser.getCodec().readTree(jsonparser); final JsonNode topicsNode = jsonparser.getCodec().readTree(jsonparser);
final List<List<String>> topics = Lists.newArrayList(); final List<List<LogTopic>> topics = Lists.newArrayList();
if (!topicsNode.isArray()) { if (!topicsNode.isArray()) {
topics.add(Collections.singletonList(topicsNode.textValue())); topics.add(singletonList(LogTopic.fromHexString(topicsNode.textValue())));
} else { } else {
for (JsonNode child : topicsNode) { for (JsonNode child : topicsNode) {
if (child.isArray()) { if (child.isArray()) {
final List<String> childItems = Lists.newArrayList(); final List<LogTopic> childItems = Lists.newArrayList();
for (JsonNode subChild : child) { for (JsonNode subChild : child) {
childItems.add(subChild.textValue()); childItems.add(LogTopic.fromHexString(subChild.textValue()));
} }
topics.add(childItems); topics.add(childItems);
} else { } else {
topics.add(Collections.singletonList(child.textValue())); topics.add(singletonList(LogTopic.fromHexString(child.textValue())));
} }
} }
} }
return new TopicsParameter(topics); return topics;
} }
} }

@ -39,7 +39,7 @@ public class SubscriptionBuilder {
return new LogsSubscription( return new LogsSubscription(
subscriptionId, subscriptionId,
connectionId, connectionId,
Optional.ofNullable(request.getFilterParameter()) Optional.ofNullable(request.getLogsQuery())
.orElseThrow(IllegalArgumentException::new)); .orElseThrow(IllegalArgumentException::new));
} }
case SYNCING: case SYNCING:

@ -14,25 +14,21 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.logs; 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.Subscription;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType;
import org.hyperledger.besu.ethereum.api.query.LogsQuery; import org.hyperledger.besu.ethereum.api.query.LogsQuery;
public class LogsSubscription extends Subscription { public class LogsSubscription extends Subscription {
private final FilterParameter filterParameter; private final LogsQuery logsQuery;
public LogsSubscription( 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); super(subscriptionId, connectionId, SubscriptionType.LOGS, Boolean.FALSE);
this.filterParameter = filterParameter; this.logsQuery = logsQuery;
} }
public LogsQuery getLogsQuery() { public LogsQuery getLogsQuery() {
return new LogsQuery.Builder() return logsQuery;
.addresses(filterParameter.getAddresses())
.topics(filterParameter.getTopics())
.build();
} }
} }

@ -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<String> address;
private final List<String> topics;
@JsonCreator
LogsSubscriptionParam(
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address")
final List<String> address,
@JsonProperty("topics") final List<String> topics) {
this.address = address;
this.topics = topics;
}
List<String> address() {
return address;
}
List<String> topics() {
return topics;
}
}

@ -14,7 +14,7 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; 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; import java.util.Objects;
@ -22,17 +22,17 @@ public class SubscribeRequest {
private final SubscriptionType subscriptionType; private final SubscriptionType subscriptionType;
private final Boolean includeTransaction; private final Boolean includeTransaction;
private final FilterParameter filterParameter; private final LogsQuery logsQuery;
private final String connectionId; private final String connectionId;
public SubscribeRequest( public SubscribeRequest(
final SubscriptionType subscriptionType, final SubscriptionType subscriptionType,
final FilterParameter filterParameter, final LogsQuery logsQuery,
final Boolean includeTransaction, final Boolean includeTransaction,
final String connectionId) { final String connectionId) {
this.subscriptionType = subscriptionType; this.subscriptionType = subscriptionType;
this.includeTransaction = includeTransaction; this.includeTransaction = includeTransaction;
this.filterParameter = filterParameter; this.logsQuery = logsQuery;
this.connectionId = connectionId; this.connectionId = connectionId;
} }
@ -40,8 +40,8 @@ public class SubscribeRequest {
return subscriptionType; return subscriptionType;
} }
public FilterParameter getFilterParameter() { public LogsQuery getLogsQuery() {
return filterParameter; return logsQuery;
} }
public Boolean getIncludeTransaction() { public Boolean getIncludeTransaction() {
@ -54,16 +54,9 @@ public class SubscribeRequest {
@Override @Override
public String toString() { public String toString() {
return "SubscribeRequest{" return String.format(
+ "subscriptionType=" "SubscribeRequest{subscriptionType=%s, includeTransaction=%s, logsQuery=%s, connectionId=%s}",
+ subscriptionType subscriptionType, includeTransaction, logsQuery, connectionId);
+ ", includeTransaction="
+ includeTransaction
+ ", filterParameter="
+ filterParameter
+ ", connectionId="
+ connectionId
+ '}';
} }
@Override @Override
@ -77,12 +70,12 @@ public class SubscribeRequest {
final SubscribeRequest that = (SubscribeRequest) o; final SubscribeRequest that = (SubscribeRequest) o;
return subscriptionType == that.subscriptionType return subscriptionType == that.subscriptionType
&& Objects.equals(includeTransaction, that.includeTransaction) && Objects.equals(includeTransaction, that.includeTransaction)
&& Objects.equals(filterParameter, that.filterParameter) && Objects.equals(logsQuery, that.logsQuery)
&& Objects.equals(connectionId, that.connectionId); && Objects.equals(connectionId, that.connectionId);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(subscriptionType, includeTransaction, filterParameter, connectionId); return Objects.hash(subscriptionType, includeTransaction, logsQuery, connectionId);
} }
} }

@ -15,15 +15,11 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; 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.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.JsonRpcParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.UnsignedLongParameter; 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.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; import java.util.Optional;
public class SubscriptionRequestMapper { public class SubscriptionRequestMapper {
@ -77,31 +73,8 @@ public class SubscriptionRequestMapper {
private SubscribeRequest parseLogsRequest( private SubscribeRequest parseLogsRequest(
final WebSocketRpcRequest request, final JsonRpcParameter parameter) { final WebSocketRpcRequest request, final JsonRpcParameter parameter) {
final LogsSubscriptionParam logFilterParams = final LogsQuery logsQuery = parameter.required(request.getParams(), 1, LogsQuery.class);
parameter.required(request.getParams(), 1, LogsSubscriptionParam.class); return new SubscribeRequest(SubscriptionType.LOGS, logsQuery, null, request.getConnectionId());
return new SubscribeRequest(
SubscriptionType.LOGS,
createFilterParameter(logFilterParams),
null,
request.getConnectionId());
}
private FilterParameter createFilterParameter(final LogsSubscriptionParam logFilterParams) {
final List<String> addresses = hasAddresses(logFilterParams);
final List<List<String>> topics = hasTopics(logFilterParams);
return new FilterParameter(null, null, addresses, new TopicsParameter(topics), null);
}
private List<String> hasAddresses(final LogsSubscriptionParam logFilterParams) {
return logFilterParams.address() != null && !logFilterParams.address().isEmpty()
? logFilterParams.address()
: Collections.emptyList();
}
private List<List<String>> hasTopics(final LogsSubscriptionParam logFilterParams) {
return logFilterParams.topics() != null && !logFilterParams.topics().isEmpty()
? Arrays.asList(logFilterParams.topics())
: Collections.emptyList();
} }
public UnsubscribeRequest mapUnsubscribeRequest(final JsonRpcRequest jsonRpcRequest) public UnsubscribeRequest mapUnsubscribeRequest(final JsonRpcRequest jsonRpcRequest)

@ -16,6 +16,10 @@
*/ */
package org.hyperledger.besu.ethereum.api.query; 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.Address;
import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.Log;
import org.hyperledger.besu.ethereum.core.LogTopic; import org.hyperledger.besu.ethereum.core.LogTopic;
@ -25,32 +29,40 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; 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; import com.google.common.collect.Lists;
public class LogsQuery { public class LogsQuery {
private final List<Address> queryAddresses; private final List<Address> addresses;
private final List<List<LogTopic>> queryTopics; private final List<List<LogTopic>> topics;
private final List<LogsBloomFilter> addressBlooms; private final List<LogsBloomFilter> addressBlooms;
private final List<List<LogsBloomFilter>> topicsBlooms; private final List<List<LogsBloomFilter>> topicsBlooms;
private LogsQuery(final List<Address> queryAddresses, final List<List<LogTopic>> queryTopics) { @JsonCreator
this.queryAddresses = queryAddresses; public LogsQuery(
this.queryTopics = queryTopics; @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @JsonProperty("address")
addressBlooms = final List<Address> addresses,
this.queryAddresses.stream() @JsonDeserialize(using = TopicsDeserializer.class) @JsonProperty("topics")
.map(LogsBloomFilter::computeBytes) final List<List<LogTopic>> topics) {
.collect(Collectors.toList()); this.addresses = addresses != null ? addresses : emptyList();
topicsBlooms = this.topics = topics != null ? topics : emptyList();
this.queryTopics.stream() this.addressBlooms =
this.addresses.stream().map(LogsBloomFilter::computeBytes).collect(toUnmodifiableList());
this.topicsBlooms =
this.topics.stream()
.map( .map(
topics -> subTopics ->
topics.stream() subTopics.stream()
.filter(Objects::nonNull) .filter(Objects::nonNull)
.map(LogsBloomFilter::computeBytes) .map(LogsBloomFilter::computeBytes)
.collect(Collectors.toList())) .collect(Collectors.toList()))
.collect(Collectors.toList()); .collect(toUnmodifiableList());
} }
public boolean couldMatch(final LogsBloomFilter bloom) { public boolean couldMatch(final LogsBloomFilter bloom) {
@ -66,22 +78,14 @@ public class LogsQuery {
} }
private boolean matchesAddresses(final Address address) { private boolean matchesAddresses(final Address address) {
return queryAddresses.isEmpty() || queryAddresses.contains(address); return addresses.isEmpty() || addresses.contains(address);
} }
private boolean matchesTopics(final List<LogTopic> topics) { private boolean matchesTopics(final List<LogTopic> topics) {
if (queryTopics.isEmpty()) { return this.topics.isEmpty()
return true; || (topics.size() >= this.topics.size()
} && IntStream.range(0, this.topics.size())
if (topics.size() < queryTopics.size()) { .allMatch(i -> matchesTopic(topics.get(i), this.topics.get(i))));
return false;
}
for (int i = 0; i < queryTopics.size(); ++i) {
if (!matchesTopic(topics.get(i), queryTopics.get(i))) {
return false;
}
}
return true;
} }
private boolean matchesTopic(final LogTopic topic, final List<LogTopic> matchCriteria) { private boolean matchesTopic(final LogTopic topic, final List<LogTopic> matchCriteria) {
@ -93,13 +97,19 @@ public class LogsQuery {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
final LogsQuery logsQuery = (LogsQuery) o; final LogsQuery logsQuery = (LogsQuery) o;
return Objects.equals(queryAddresses, logsQuery.queryAddresses) return Objects.equals(addresses, logsQuery.addresses)
&& Objects.equals(queryTopics, logsQuery.queryTopics); && Objects.equals(topics, logsQuery.topics);
}
@Override
public String toString() {
return String.format(
"%s{addresses=%s, topics=%s", getClass().getSimpleName(), addresses, topics);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(queryAddresses, queryTopics); return Objects.hash(addresses, topics);
} }
public static class Builder { public static class Builder {
@ -134,13 +144,6 @@ public class LogsQuery {
return this; return this;
} }
public Builder topics(final TopicsParameter topicsParameter) {
if (topicsParameter != null) {
topics(topicsParameter.getTopics());
}
return this;
}
public LogsQuery build() { public LogsQuery build() {
return new LogsQuery(queryAddresses, queryTopics); return new LogsQuery(queryAddresses, queryTopics);
} }

@ -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<List<LogTopic>> queryTopics = new ArrayList<>();
public TopicsParameter(final List<List<String>> topics) {
if (topics != null) {
for (final List<String> list : topics) {
final List<LogTopic> 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<List<LogTopic>> getTopics() {
return queryTopics;
}
@Override
public String toString() {
return "TopicsParameter{" + "queryTopics=" + queryTopics + '}';
}
}

@ -14,6 +14,8 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods; 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.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@ -29,11 +31,12 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonRpcPara
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; 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.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.query.LogsQuery; 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.Address;
import org.hyperledger.besu.ethereum.core.LogTopic;
import java.util.Arrays; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -98,13 +101,12 @@ public class EthNewFilterTest {
@Test @Test
public void newFilterWithTopicsOnlyParamInstallsExpectedLogFilter() { public void newFilterWithTopicsOnlyParamInstallsExpectedLogFilter() {
final List<List<String>> topics = topics(); final FilterParameter filterParameter = filterParamWithAddressAndTopics(null, topics());
final FilterParameter filterParameter = filterParamWithAddressAndTopics(null, topics);
final JsonRpcRequest request = ethNewFilter(filterParameter); final JsonRpcRequest request = ethNewFilter(filterParameter);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1"); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1");
final LogsQuery expectedLogsQuery = 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"); when(filterManager.installLogFilter(any(), any(), eq(expectedLogsQuery))).thenReturn("0x1");
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
@ -136,13 +138,13 @@ public class EthNewFilterTest {
@Test @Test
public void newFilterWithAddressAndTopicsParamInstallsExpectedLogFilter() { public void newFilterWithAddressAndTopicsParamInstallsExpectedLogFilter() {
final Address address = Address.fromHexString("0x0"); final Address address = Address.fromHexString("0x0");
final List<List<String>> topics = topics(); final List<List<LogTopic>> topics = topics();
final FilterParameter filterParameter = filterParamWithAddressAndTopics(address, topics); final FilterParameter filterParameter = filterParamWithAddressAndTopics(address, topics);
final JsonRpcRequest request = ethNewFilter(filterParameter); final JsonRpcRequest request = ethNewFilter(filterParameter);
final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1"); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(request.getId(), "0x1");
final LogsQuery expectedLogsQuery = 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"); when(filterManager.installLogFilter(any(), any(), eq(expectedLogsQuery))).thenReturn("0x1");
final JsonRpcResponse actualResponse = method.response(request); final JsonRpcResponse actualResponse = method.response(request);
@ -153,15 +155,21 @@ public class EthNewFilterTest {
refEq(blockParamLatest()), refEq(blockParamLatest()), eq(expectedLogsQuery)); refEq(blockParamLatest()), refEq(blockParamLatest()), eq(expectedLogsQuery));
} }
private List<List<String>> topics() { private List<List<LogTopic>> topics() {
return Arrays.asList( return singletonList(
Arrays.asList("0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")); singletonList(
LogTopic.fromHexString(
"0x000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")));
} }
private FilterParameter filterParamWithAddressAndTopics( private FilterParameter filterParamWithAddressAndTopics(
final Address address, final List<List<String>> topics) { final Address address, final List<List<LogTopic>> topics) {
final List<String> addresses = address != null ? Arrays.asList(address.toString()) : null; return new FilterParameter(
return new FilterParameter("latest", "latest", addresses, new TopicsParameter(topics), null); "latest",
"latest",
Optional.ofNullable(address).map(Collections::singletonList).orElse(emptyList()),
topics,
null);
} }
private JsonRpcRequest ethNewFilter(final FilterParameter filterParameter) { private JsonRpcRequest ethNewFilter(final FilterParameter filterParameter) {

@ -16,10 +16,11 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toUnmodifiableList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; 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 org.hyperledger.besu.ethereum.core.LogTopic;
import java.util.Arrays; import java.util.Arrays;
@ -142,7 +143,7 @@ public class FilterParameterTest {
"0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74"); "0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74");
final FilterParameter filter = createFilterWithTopics(topics); final FilterParameter filter = createFilterWithTopics(topics);
assertThat(filter.getTopics().getTopics()) assertThat(filter.getTopics())
.containsExactly( .containsExactly(
singletonList( singletonList(
LogTopic.fromHexString( LogTopic.fromHexString(
@ -164,7 +165,7 @@ public class FilterParameterTest {
final FilterParameter filter = createFilterWithTopics(topics); final FilterParameter filter = createFilterWithTopics(topics);
assertThat(filter.getTopics().getTopics()) assertThat(filter.getTopics())
.containsExactly( .containsExactly(
singletonList( singletonList(
LogTopic.fromHexString( LogTopic.fromHexString(
@ -185,7 +186,7 @@ public class FilterParameterTest {
singletonList("0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74")); singletonList("0x000000000000000000000000244a53ab66ea8901c25efc48c8ab84662643cc74"));
final FilterParameter filter = createFilterWithTopics(topics); final FilterParameter filter = createFilterWithTopics(topics);
assertThat(filter.getTopics().getTopics()) assertThat(filter.getTopics())
.containsExactly( .containsExactly(
singletonList( singletonList(
LogTopic.fromHexString( LogTopic.fromHexString(
@ -201,7 +202,7 @@ public class FilterParameterTest {
final List<String> topics = emptyList(); final List<String> topics = emptyList();
final FilterParameter filter = createFilterWithTopics(topics); final FilterParameter filter = createFilterWithTopics(topics);
assertThat(filter.getTopics().getTopics().size()).isZero(); assertThat(filter.getTopics().size()).isZero();
} }
@Test @Test
@ -211,7 +212,7 @@ public class FilterParameterTest {
singletonList("0xce8688f853ffa65c042b72302433c25d7a230c322caba0901587534b6551091d"), singletonList("0xce8688f853ffa65c042b72302433c25d7a230c322caba0901587534b6551091d"),
emptyList()); emptyList());
final FilterParameter filter = createFilterWithTopics(topics); final FilterParameter filter = createFilterWithTopics(topics);
assertThat(filter.getTopics().getTopics()) assertThat(filter.getTopics())
.containsExactly( .containsExactly(
singletonList( singletonList(
LogTopic.fromHexString( LogTopic.fromHexString(
@ -229,7 +230,12 @@ public class FilterParameterTest {
} }
private FilterParameter filterParameterWithAddresses(final String... addresses) { 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( private FilterParameter filterParameterWithAddressAndSingleListOfTopics(
@ -237,17 +243,19 @@ public class FilterParameterTest {
return new FilterParameter( return new FilterParameter(
"latest", "latest",
"latest", "latest",
Arrays.asList(address), singletonList(Address.fromHexString(address)),
new TopicsParameter(singletonList(Arrays.asList(topics))), singletonList(
Arrays.stream(topics).map(LogTopic::fromHexString).collect(toUnmodifiableList())),
null); null);
} }
private FilterParameter filterParameterWithAddressAndMultipleListOfTopics( private FilterParameter filterParameterWithAddressAndMultipleListOfTopics(
final String address, final String... topics) { final String address, final String... topics) {
List<String> topicsList = Arrays.asList(topics); List<LogTopic> topicsList =
List<List<String>> topicsListList = Arrays.asList(topicsList, topicsList); Arrays.stream(topics).map(LogTopic::fromHexString).collect(toUnmodifiableList());
List<List<LogTopic>> topicsListList = Arrays.asList(topicsList, topicsList);
return new FilterParameter( 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) private JsonRpcRequest readJsonAsJsonRpcRequest(final String jsonWithSingleAddress)

@ -17,12 +17,12 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable; 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.blockheaders.NewBlockHeadersSubscription;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.logs.LogsSubscription; 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.SubscribeRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request.SubscriptionType; 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.jsonrpc.websocket.subscription.syncing.SyncingSubscription;
import org.hyperledger.besu.ethereum.api.query.LogsQuery;
import java.util.function.Function; import java.util.function.Function;
@ -41,11 +41,11 @@ public class SubscriptionBuilderTest {
@Test @Test
public void shouldBuildLogsSubscriptionWhenSubscribeRequestTypeIsLogs() { public void shouldBuildLogsSubscriptionWhenSubscribeRequestTypeIsLogs() {
final FilterParameter filterParameter = filterParameter(); final LogsQuery logsQuery = logsQuery();
final SubscribeRequest subscribeRequest = final SubscribeRequest subscribeRequest =
new SubscribeRequest(SubscriptionType.LOGS, filterParameter, null, CONNECTION_ID); new SubscribeRequest(SubscriptionType.LOGS, logsQuery, null, CONNECTION_ID);
final LogsSubscription expectedSubscription = final LogsSubscription expectedSubscription =
new LogsSubscription(1L, CONNECTION_ID, filterParameter); new LogsSubscription(1L, CONNECTION_ID, logsQuery);
final Subscription builtSubscription = final Subscription builtSubscription =
subscriptionBuilder.build(1L, CONNECTION_ID, subscribeRequest); subscriptionBuilder.build(1L, CONNECTION_ID, subscribeRequest);
@ -96,7 +96,7 @@ public class SubscriptionBuilderTest {
public void shouldReturnLogsSubscriptionWhenMappingLogsSubscription() { public void shouldReturnLogsSubscriptionWhenMappingLogsSubscription() {
final Function<Subscription, LogsSubscription> function = final Function<Subscription, LogsSubscription> function =
subscriptionBuilder.mapToSubscriptionClass(LogsSubscription.class); 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); assertThat(function.apply(subscription)).isInstanceOf(LogsSubscription.class);
} }
@ -147,7 +147,7 @@ public class SubscriptionBuilderTest {
"NewBlockHeadersSubscription instance can't be mapped to type LogsSubscription"); "NewBlockHeadersSubscription instance can't be mapped to type LogsSubscription");
} }
private FilterParameter filterParameter() { private LogsQuery logsQuery() {
return new FilterParameter(null, null, null, null, null); return new LogsQuery(null, null);
} }
} }

@ -21,11 +21,10 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; 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.LogResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; 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.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.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Address;
import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.Block;
@ -352,19 +351,9 @@ public class LogsSubscriptionServiceTest {
private LogsSubscription createSubscription( private LogsSubscription createSubscription(
final List<Address> addresses, final List<List<LogTopic>> logTopics) { final List<Address> addresses, final List<List<LogTopic>> logTopics) {
// TODO: FilterParameter constructor should work with proper types instead of Strings
final List<String> addressStrings =
addresses.stream().map(Address::toString).collect(Collectors.toList());
final List<List<String>> topicStrings =
logTopics.stream()
.map(topics -> topics.stream().map(LogTopic::toString).collect(Collectors.toList()))
.collect(Collectors.toList());
final FilterParameter filterParameter = return new LogsSubscription(
new FilterParameter(null, null, addressStrings, new TopicsParameter(topicStrings), null); nextSubscriptionId.incrementAndGet(), "conn", new LogsQuery(addresses, logTopics));
final LogsSubscription logsSubscription =
new LogsSubscription(nextSubscriptionId.incrementAndGet(), "conn", filterParameter);
return logsSubscription;
} }
private void registerSubscriptions(final LogsSubscription... subscriptions) { private void registerSubscriptions(final LogsSubscription... subscriptions) {

@ -14,6 +14,9 @@
*/ */
package org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.request; 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.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.both; import static org.hamcrest.CoreMatchers.both;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@ -22,13 +25,14 @@ import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; 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.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.internal.parameters.JsonRpcParameter;
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketRpcRequest; 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.List;
import java.util.Collections; import java.util.stream.Stream;
import io.vertx.core.json.Json; import io.vertx.core.json.Json;
import org.junit.Before; import org.junit.Before;
@ -158,15 +162,14 @@ public class SubscriptionRequestMapperTest {
parseWebSocketRpcRequest( parseWebSocketRpcRequest(
"{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}"); "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}");
final FilterParameter expectedFilterParam = final SubscribeRequest expectedSubscribeRequest =
new FilterParameter( new SubscribeRequest(
null, SubscriptionType.LOGS,
new LogsQuery(
singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")),
emptyList()),
null, null,
Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"),
new TopicsParameter(Collections.emptyList()),
null); null);
final SubscribeRequest expectedSubscribeRequest =
new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null);
final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest);
@ -180,20 +183,21 @@ public class SubscriptionRequestMapperTest {
parseWebSocketRpcRequest( parseWebSocketRpcRequest(
"{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": [\"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"0xf17f52151EbEF6C7334FAD080c5704D77216b732\"], \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\"]}]}"); "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": [\"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"0xf17f52151EbEF6C7334FAD080c5704D77216b732\"], \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\"]}]}");
final FilterParameter expectedFilterParam = final SubscribeRequest expectedSubscribeRequest =
new FilterParameter( new SubscribeRequest(
null, SubscriptionType.LOGS,
null, new LogsQuery(
Arrays.asList( Stream.of(
"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd", "0x8320fe7702b96808f7bbc0d4a888ed1468216cfd",
"0xf17f52151EbEF6C7334FAD080c5704D77216b732"), "0xf17f52151EbEF6C7334FAD080c5704D77216b732")
new TopicsParameter( .map(Address::fromHexString)
Arrays.asList( .collect(toUnmodifiableList()),
Arrays.asList( singletonList(
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902"))), singletonList(
LogTopic.fromHexString(
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902")))),
null,
null); null);
final SubscribeRequest expectedSubscribeRequest =
new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null);
final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest);
@ -207,19 +211,20 @@ public class SubscriptionRequestMapperTest {
parseWebSocketRpcRequest( parseWebSocketRpcRequest(
"{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\", \"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901\"]}]}"); "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\", \"topics\": [\"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902\", \"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901\"]}]}");
final FilterParameter expectedFilterParam = final SubscribeRequest expectedSubscribeRequest =
new FilterParameter( new SubscribeRequest(
null, SubscriptionType.LOGS,
new LogsQuery(
singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")),
List.of(
singletonList(
LogTopic.fromHexString(
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902")),
singletonList(
LogTopic.fromHexString(
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901")))),
null, null,
Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"),
new TopicsParameter(
Arrays.asList(
Arrays.asList(
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab902",
"0xd78a0cb8bb633d06981248b816e7bd33c2a35a6089241d099fa519e361cab901"))),
null); null);
final SubscribeRequest expectedSubscribeRequest =
new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null);
final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest);
@ -233,15 +238,14 @@ public class SubscriptionRequestMapperTest {
parseWebSocketRpcRequest( parseWebSocketRpcRequest(
"{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}"); "{\"id\": 1, \"method\": \"eth_subscribe\", \"params\": [\"logs\", {\"address\": \"0x8320fe7702b96808f7bbc0d4a888ed1468216cfd\"}]}");
final FilterParameter expectedFilterParam = final SubscribeRequest expectedSubscribeRequest =
new FilterParameter( new SubscribeRequest(
null, SubscriptionType.LOGS,
new LogsQuery(
singletonList(Address.fromHexString("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd")),
emptyList()),
null, null,
Arrays.asList("0x8320fe7702b96808f7bbc0d4a888ed1468216cfd"),
new TopicsParameter(Collections.emptyList()),
null); null);
final SubscribeRequest expectedSubscribeRequest =
new SubscribeRequest(SubscriptionType.LOGS, expectedFilterParam, null, null);
final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest); final SubscribeRequest subscribeRequest = mapper.mapSubscribeRequest(jsonRpcRequest);
@ -257,8 +261,8 @@ public class SubscriptionRequestMapperTest {
thrown.expect(InvalidSubscriptionRequestException.class); thrown.expect(InvalidSubscriptionRequestException.class);
thrown.expectCause( thrown.expectCause(
both(hasMessage(equalTo("Invalid odd-length hex binary representation 0x1"))) both(hasMessage(equalTo("Invalid json rpc parameter at index 1")))
.and(instanceOf(IllegalArgumentException.class))); .and(instanceOf(InvalidJsonRpcParameters.class)));
mapper.mapSubscribeRequest(jsonRpcRequest); mapper.mapSubscribeRequest(jsonRpcRequest);
} }

@ -44,7 +44,7 @@ public class LogTopic extends DelegatingBytesValue {
} }
public static LogTopic fromHexString(final String str) { public static LogTopic fromHexString(final String str) {
return new LogTopic(BytesValue.fromHexString(str)); return str == null ? null : LogTopic.create(BytesValue.fromHexString(str));
} }
/** /**

Loading…
Cancel
Save