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));
}
/**