From 041bbf13891cf75dcb2aeeac0e979e63f8ea5e15 Mon Sep 17 00:00:00 2001 From: Igor Artamonov Date: Mon, 21 Oct 2024 21:18:59 +0100 Subject: [PATCH] solution: move most of JSON mapping config to annotations --- etherjar-rpc-json/README.adoc | 4 + .../etherjar/rpc/json/StateDiffJson.java | 1 + .../rpc/json/StateDiffJsonDeserializer.java | 2 +- .../etherjar/rpc/json/SyncingJson.java | 1 + .../etherjar/rpc/json/TraceItemJson.java | 33 +++++- .../rpc/json/TraceItemJsonDeserializer.java | 100 ------------------ .../rpc/json/TransactionCallJson.java | 8 +- .../json/TransactionCallJsonSerializer.java | 69 ------------ .../etherjar/rpc/json/WithdrawalJson.java | 2 + .../rpc/JacksonEthRpcConverterSpec.groovy | 4 +- 10 files changed, 48 insertions(+), 176 deletions(-) create mode 100644 etherjar-rpc-json/README.adoc delete mode 100644 etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJsonDeserializer.java delete mode 100644 etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJsonSerializer.java diff --git a/etherjar-rpc-json/README.adoc b/etherjar-rpc-json/README.adoc new file mode 100644 index 00000000..e0ddfd05 --- /dev/null +++ b/etherjar-rpc-json/README.adoc @@ -0,0 +1,4 @@ += JSON Mapping for RPC API + +.Reference: +- https://github.com/ethereum/execution-apis/tree/main/src/schemas diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJson.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJson.java index b9403db7..ade4ccb8 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJson.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJson.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.Map; +// Uses a custom deserializer to read the JSON object, b/c the structure is a bit too complex for annotations @JsonDeserialize(using = StateDiffJsonDeserializer.class) public class StateDiffJson { diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJsonDeserializer.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJsonDeserializer.java index a91c49f3..ede7f125 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJsonDeserializer.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/StateDiffJsonDeserializer.java @@ -33,7 +33,7 @@ public class StateDiffJsonDeserializer extends JsonDeserializer { - protected static final Function WEI_CONVERTER = Wei::from; + protected static final Function WEI_CONVERTER = Wei::fromHex; protected static final Function CODE_CONVERTER = HexData::from; protected static final Function NONCE_CONVERTER = (s) -> HexQuantity.from(s).getValue().longValueExact(); protected static final Function STORAGE_CONVERTER = Hex32::from; diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/SyncingJson.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/SyncingJson.java index 3f7fd851..ccc5dbe0 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/SyncingJson.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/SyncingJson.java @@ -18,6 +18,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +// Uses a custom serialization because it's either a boolean or an object @JsonDeserialize(using = SyncingJsonDeserializer.class) @JsonSerialize(using = SyncingJsonSerializer.class) public class SyncingJson { diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJson.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJson.java index a21b7802..b8409d75 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJson.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJson.java @@ -17,7 +17,10 @@ package io.emeraldpay.etherjar.rpc.json; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.emeraldpay.etherjar.domain.Address; import io.emeraldpay.etherjar.domain.BlockHash; import io.emeraldpay.etherjar.domain.TransactionId; @@ -27,18 +30,24 @@ import java.io.Serializable; import java.util.List; -@JsonDeserialize(using = TraceItemJsonDeserializer.class) +@JsonIgnoreProperties(ignoreUnknown = true) public class TraceItemJson implements Serializable { private TraceType type; private Action action; private BlockHash blockHash; + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long blockNumber; private Result result; private String error; private Long subtraces; + @JsonDeserialize(contentUsing = HexLongDeserializer.class) + @JsonSerialize(contentUsing = HexLongSerializer.class) private List traceAddress; private TransactionId transactionHash; + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long transactionPosition; public Action getAction() { @@ -157,6 +166,8 @@ public static class Action implements Serializable { private CallType callType; private Address from; + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long gas; private HexData init; private HexData input; @@ -284,14 +295,30 @@ public int hashCode() { } public static enum CallType { - NONE, CALL, CALLCODE, DELEGATECALL, STATICCALL; + @JsonProperty("none") + NONE, + @JsonProperty("call") + CALL, + @JsonProperty("callcode") + CALLCODE, + @JsonProperty("delegatecall") + DELEGATECALL, + @JsonProperty("staticcall") + STATICCALL; } public static enum TraceType { - CREATE, CALL, SUICIDE; + @JsonProperty("create") + CREATE, + @JsonProperty("call") + CALL, + @JsonProperty("suicide") + SUICIDE; } public static class Result implements Serializable { + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long gasUsed; private HexData output; private Address address; diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJsonDeserializer.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJsonDeserializer.java deleted file mode 100644 index 63e66f23..00000000 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TraceItemJsonDeserializer.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2020 EmeraldPay Inc, All Rights Reserved. - * Copyright (c) 2016-2017 Infinitape Inc, All Rights Reserved. - * - * 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. - */ - -package io.emeraldpay.etherjar.rpc.json; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class TraceItemJsonDeserializer extends EtherJsonDeserializer { - - @Override - public TraceItemJson deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - JsonNode node = jp.readValueAsTree(); - TraceItemJson trace = new TraceItemJson(); - - JsonNode typeNode = node.get("type"); - if (typeNode != null) { - String type = typeNode.asText(); - if (type != null) { - trace.setType(TraceItemJson.TraceType.valueOf(type.toUpperCase())); - } - } - - JsonNode actionNode = node.get("action"); - if (actionNode != null && actionNode.isObject()) { - TraceItemJson.Action action = new TraceItemJson.Action(); - trace.setAction(action); - - JsonNode callType = actionNode.get("callType"); - if (callType != null) { - String name = callType.asText(); - if (name != null && name.length() > 0) { - action.setCallType(TraceItemJson.CallType.valueOf(name.toUpperCase())); - } - } - action.setFrom(getAddress(actionNode, "from")); - action.setGas(getLong(actionNode, "gas")); - action.setInput(getData(actionNode, "input")); - action.setTo(getAddress(actionNode, "to")); - action.setValue(getWei(actionNode, "value")); - action.setInit(getData(actionNode, "init")); - action.setAddress(getAddress(actionNode, "address")); - action.setBalance(getWei(actionNode, "balance")); - action.setRefundAddress(getAddress(actionNode, "refundAddress")); - - } - trace.setBlockHash(getBlockHash(node, "blockHash")); - trace.setBlockNumber(getLong(node, "blockNumber")); - - JsonNode resultNode = node.get("result"); - if (resultNode != null && resultNode.isObject()) { - TraceItemJson.Result result = new TraceItemJson.Result(); - trace.setResult(result); - - result.setGasUsed(getLong(resultNode, "gasUsed")); - result.setOutput(getData(resultNode, "output")); - - result.setAddress(getAddress(resultNode, "address")); - result.setCode(getData(resultNode, "code")); - } - JsonNode errorNode = node.get("error"); - if (errorNode != null) { - trace.setError(errorNode.textValue()); - } - trace.setSubtraces(getLong(node, "subtraces")); - JsonNode traceAddrNode = node.get("traceAddress"); - if (traceAddrNode != null && traceAddrNode.isArray()) { - List traceAddr = new ArrayList<>(traceAddrNode.size()); - for (JsonNode el : traceAddrNode) { - traceAddr.add(getLong(el)); - } - trace.setTraceAddress(traceAddr); - } - - trace.setTransactionHash(getTxHash(node, "transactionHash")); - trace.setTransactionPosition(getLong(node, "transactionPosition")); - - return trace; - } -} diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJson.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJson.java index 7f4e1ca4..8a9f02bb 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJson.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJson.java @@ -17,6 +17,8 @@ package io.emeraldpay.etherjar.rpc.json; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.emeraldpay.etherjar.domain.Address; import io.emeraldpay.etherjar.domain.Wei; @@ -24,15 +26,19 @@ import java.io.Serializable; -@JsonSerialize(using = TransactionCallJsonSerializer.class) +@JsonInclude(JsonInclude.Include.NON_NULL) public class TransactionCallJson implements Serializable { private Address from; private Address to; + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long gas; private Wei gasPrice; private Wei value; private HexData data; + @JsonDeserialize(using = HexLongDeserializer.class) + @JsonSerialize(using = HexLongSerializer.class) private Long nonce; public TransactionCallJson() { diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJsonSerializer.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJsonSerializer.java deleted file mode 100644 index f0616447..00000000 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/TransactionCallJsonSerializer.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2020 EmeraldPay Inc, All Rights Reserved. - * Copyright (c) 2016-2017 Infinitape Inc, All Rights Reserved. - * - * 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. - */ - -package io.emeraldpay.etherjar.rpc.json; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; -import io.emeraldpay.etherjar.hex.HexEncoding; - -import java.io.IOException; -import java.math.BigInteger; - -public class TransactionCallJsonSerializer extends JsonSerializer { - - @Override - public void serialize(TransactionCallJson value, JsonGenerator gen, SerializerProvider serializers) - throws IOException, JsonProcessingException { - if (value == null) { - gen.writeNull(); - return; - } - gen.writeStartObject(); - if (value.getFrom() != null) { - gen.writeFieldName("from"); - gen.writeString(value.getFrom().toHex()); - } - if (value.getTo() != null) { - gen.writeFieldName("to"); - gen.writeString(value.getTo().toHex()); - } - if (value.getGas() != null) { - gen.writeFieldName("gas"); - gen.writeString(HexEncoding.toHex(BigInteger.valueOf(value.getGas()))); - } - if (value.getGasPrice() != null) { - gen.writeFieldName("gasPrice"); - gen.writeString(HexEncoding.toHex(value.getGasPrice().getAmount())); - } - if (value.getValue() != null) { - gen.writeFieldName("value"); - gen.writeString(HexEncoding.toHex(value.getValue().getAmount())); - } - if (value.getData() != null) { - gen.writeFieldName("data"); - gen.writeString(value.getData().toHex()); - } - if (value.getNonce() != null) { - gen.writeFieldName("nonce"); - gen.writeString(HexEncoding.toHex(value.getNonce())); - } - gen.writeEndObject(); - } -} diff --git a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/WithdrawalJson.java b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/WithdrawalJson.java index 117a8306..7000c9db 100644 --- a/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/WithdrawalJson.java +++ b/etherjar-rpc-json/src/main/java/io/emeraldpay/etherjar/rpc/json/WithdrawalJson.java @@ -1,5 +1,6 @@ package io.emeraldpay.etherjar.rpc.json; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import io.emeraldpay.etherjar.domain.Address; import io.emeraldpay.etherjar.domain.Wei; @@ -9,6 +10,7 @@ /** * @see https://github.com/ethereum/execution-apis/blob/main/src/schemas/withdrawal.yaml */ +@JsonIgnoreProperties(ignoreUnknown = true) public class WithdrawalJson { @JsonDeserialize(using = HexLongDeserializer.class) private Long index; diff --git a/etherjar-rpc-json/src/test/groovy/io/emeraldpay/etherjar/rpc/JacksonEthRpcConverterSpec.groovy b/etherjar-rpc-json/src/test/groovy/io/emeraldpay/etherjar/rpc/JacksonEthRpcConverterSpec.groovy index 637be11e..a60121b2 100644 --- a/etherjar-rpc-json/src/test/groovy/io/emeraldpay/etherjar/rpc/JacksonEthRpcConverterSpec.groovy +++ b/etherjar-rpc-json/src/test/groovy/io/emeraldpay/etherjar/rpc/JacksonEthRpcConverterSpec.groovy @@ -70,8 +70,8 @@ class JacksonEthRpcConverterSpec extends Specification { act == '{"jsonrpc":"2.0","method":"eth_call","params":[' + '{"from":"0xb7819ff807d9d52a9ce5d713dc7053e8871e077b",' + '"to":"0x57d90b64a1a57749b0f932f1a3395792e12e7055",' + - '"gas":"0x0186a0",' + - '"gasPrice":"0x071afd498d0000",' + + '"gas":"0x186a0",' + + '"gasPrice":"0x71afd498d0000",' + '"value":"0x14d1120d7b160000",' + '"data":"0xa9059cbb00000000000000000000000014dd45d07d1d700579a9b7cfb3a4536890aafdc2"}' + ',"latest"],"id":1}'