From 5de4e3198c6849cb4c68515c9305e9b746cd2dd7 Mon Sep 17 00:00:00 2001 From: Nazarii Denha Date: Thu, 10 Oct 2024 15:21:36 +0200 Subject: [PATCH 1/4] add support for BlobTx for eth_signTransaction --- .../EthSendTransactionJsonParameters.java | 23 ++++++++ .../core/service/jsonrpc/RpcUtil.java | 6 ++ .../transaction/EthTransaction.java | 20 ++++++- .../transaction/PrivateTransaction.java | 5 ++ .../transaction/Transaction.java | 2 + .../signing/TransactionSerializer.java | 21 ++++++- .../EthSendTransactionJsonParametersTest.java | 42 ++++++++++++++ .../transaction/EthTransactionTest.java | 57 +++++++++++++++++++ 8 files changed, 174 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java index 736744873..df7c320d2 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java @@ -13,15 +13,18 @@ package tech.pegasys.web3signer.core.service.jsonrpc; import static tech.pegasys.web3signer.core.service.jsonrpc.RpcUtil.decodeBigInteger; +import static tech.pegasys.web3signer.core.service.jsonrpc.RpcUtil.decodeBytesList; import static tech.pegasys.web3signer.core.service.jsonrpc.RpcUtil.validateNotEmpty; import java.math.BigInteger; +import java.util.List; import java.util.Optional; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSetter; +import org.apache.tuweni.bytes.Bytes; @JsonIgnoreProperties(ignoreUnknown = true) public class EthSendTransactionJsonParameters { @@ -34,6 +37,8 @@ public class EthSendTransactionJsonParameters { private String data; private BigInteger maxFeePerGas; private BigInteger maxPriorityFeePerGas; + private BigInteger maxFeePerBlobGas; + private List versionedHashes; @JsonCreator public EthSendTransactionJsonParameters(@JsonProperty("from") final String sender) { @@ -81,6 +86,16 @@ public void maxFeePerGas(final String maxFeePerGas) { this.maxFeePerGas = decodeBigInteger(maxFeePerGas); } + @JsonSetter("maxFeePerBlobGas") + public void maxFeePerBlobGas(final String maxFeePerBlobGas) { + this.maxFeePerBlobGas = decodeBigInteger(maxFeePerBlobGas); + } + + @JsonSetter("versionedHashes") + public void versionedHashes(final List versionedHashes) { + this.versionedHashes = decodeBytesList(versionedHashes); + } + public Optional data() { return Optional.ofNullable(data); } @@ -116,4 +131,12 @@ public Optional maxPriorityFeePerGas() { public Optional maxFeePerGas() { return Optional.ofNullable(maxFeePerGas); } + + public Optional maxFeePerBlobGas() { + return Optional.ofNullable(maxFeePerBlobGas); + } + + public Optional> versionedHashes() { + return Optional.ofNullable(versionedHashes); + } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/RpcUtil.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/RpcUtil.java index 32d5c524f..129d08ae0 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/RpcUtil.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/RpcUtil.java @@ -19,10 +19,12 @@ import java.math.BigInteger; import java.util.List; +import java.util.stream.Collectors; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.DecodeException; import io.vertx.core.json.JsonObject; +import org.apache.tuweni.bytes.Bytes; public class RpcUtil { public static final String JSON_RPC_VERSION = "2.0"; @@ -58,6 +60,10 @@ static BigInteger decodeBigInteger(final String value) { return value == null ? null : decodeQuantity(value); } + static List decodeBytesList(final List value) { + return value.stream().map(Bytes::fromHexString).collect(Collectors.toList()); + } + public static JsonRpcError determineErrorCode(final String body, final JsonDecoder decoder) { try { final JsonRpcErrorResponse response = diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java index a64d9ba44..e6b20678c 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java @@ -98,6 +98,12 @@ public boolean isEip1559() { && transactionJsonParameters.maxFeePerGas().isPresent(); } + @Override + public boolean isEip4844() { + return transactionJsonParameters.maxFeePerBlobGas().isPresent() + && transactionJsonParameters.versionedHashes().isPresent(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -110,7 +116,19 @@ public String toString() { } protected RawTransaction createTransaction() { - if (isEip1559()) { + if (isEip4844()) { + return RawTransaction.createTransaction( + chainId, + nonce, + transactionJsonParameters.maxPriorityFeePerGas().orElseThrow(), + transactionJsonParameters.maxFeePerGas().orElseThrow(), + transactionJsonParameters.gas().orElse(DEFAULT_GAS), + transactionJsonParameters.receiver().orElse(DEFAULT_TO), + transactionJsonParameters.value().orElse(DEFAULT_VALUE), + transactionJsonParameters.data().orElse(DEFAULT_DATA), + transactionJsonParameters.maxFeePerBlobGas().orElseThrow(), + transactionJsonParameters.versionedHashes().orElseThrow()); + } else if (isEip1559()) { return RawTransaction.createTransaction( chainId, nonce, diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java index 0bf05625b..ac853fe2c 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java @@ -97,6 +97,11 @@ public boolean isEip1559() { && transactionJsonParameters.maxFeePerGas().isPresent(); } + @Override + public boolean isEip4844() { + return false; + } + @Override public String toString() { return MoreObjects.toStringHelper(this) diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java index d3f419660..fb4a8676a 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java @@ -60,4 +60,6 @@ static JsonRpcRequest jsonRpcRequest( JsonRpcRequestId getId(); boolean isEip1559(); + + boolean isEip4844(); } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java index 233fad7f9..a4f3f44eb 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java @@ -42,7 +42,9 @@ public TransactionSerializer( } public String serialize(final Transaction transaction) { - if (transaction.isEip1559()) { + if (transaction.isEip4844()) { + return toHexString(serializeEip4844(transaction)); + } else if (transaction.isEip1559()) { return toHexString(serializeEip1559(transaction)); } else { return toHexString(serializeFrontier(transaction)); @@ -77,6 +79,23 @@ private static byte[] prependEip1559TransactionType(byte[] bytesToSign) { .array(); } + private byte[] serializeEip4844(final Transaction transaction) { + byte[] bytesToSign = transaction.rlpEncode(null); + bytesToSign = prependEip4844TransactionType(bytesToSign); + + final SignatureData signatureData = sign(transaction.sender(), bytesToSign); + + byte[] serializedBytes = transaction.rlpEncode(signatureData); + return prependEip4844TransactionType(serializedBytes); + } + + private static byte[] prependEip4844TransactionType(byte[] bytesToSign) { + return ByteBuffer.allocate(bytesToSign.length + 1) + .put(TransactionType.EIP4844.getRlpType()) + .put(bytesToSign) + .array(); + } + private SignatureData sign(final String eth1Address, final byte[] bytesToSign) { final SecpArtifactSignature artifactSignature = secpSigner diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java index 6e922fb6c..873569d0b 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java @@ -18,9 +18,11 @@ import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.transaction.TransactionFactory; import java.math.BigInteger; +import java.util.List; import java.util.Optional; import io.vertx.core.json.JsonObject; +import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,6 +42,13 @@ private Optional getStringAsOptionalBigInteger( return Optional.of(new BigInteger(value.substring(2), 16)); } + private Optional> getListAsOptionalBytesList( + final JsonObject object, final String key) { + final List value; + value = object.getJsonArray(key).getList(); + return Optional.of(RpcUtil.decodeBytesList(value)); + } + @Test public void transactionStoredInJsonArrayCanBeDecoded() throws Throwable { final JsonObject parameters = validEthTransactionParameters(); @@ -75,6 +84,29 @@ public void eip1559TransactionStoredInJsonArrayCanBeDecoded() { .isEqualTo(getStringAsOptionalBigInteger(parameters, "maxFeePerGas")); } + @Test + public void eip4844TransactionStoredInJsonArrayCanBeDecoded() { + final JsonObject parameters = validEip4844EthTransactionParameters(); + + final JsonRpcRequest request = wrapParametersInRequest(parameters); + final EthSendTransactionJsonParameters txnParams = + factory.fromRpcRequestToJsonParam(EthSendTransactionJsonParameters.class, request); + + assertThat(txnParams.gas()).isEqualTo(getStringAsOptionalBigInteger(parameters, "gas")); + assertThat(txnParams.gasPrice()).isEmpty(); + assertThat(txnParams.nonce()).isEqualTo(getStringAsOptionalBigInteger(parameters, "nonce")); + assertThat(txnParams.receiver()).isEqualTo(Optional.of(parameters.getString("to"))); + assertThat(txnParams.value()).isEqualTo(getStringAsOptionalBigInteger(parameters, "value")); + assertThat(txnParams.maxPriorityFeePerGas()) + .isEqualTo(getStringAsOptionalBigInteger(parameters, "maxPriorityFeePerGas")); + assertThat(txnParams.maxFeePerGas()) + .isEqualTo(getStringAsOptionalBigInteger(parameters, "maxFeePerGas")); + assertThat(txnParams.maxFeePerBlobGas()) + .isEqualTo(getStringAsOptionalBigInteger(parameters, "maxFeePerBlobGas")); + assertThat(txnParams.versionedHashes()) + .isEqualTo(getListAsOptionalBytesList(parameters, "versionedHashes")); + } + @Test public void transactionNotStoredInJsonArrayCanBeDecoded() throws Throwable { final JsonObject parameters = validEthTransactionParameters(); @@ -139,6 +171,16 @@ private JsonObject validEip1559EthTransactionParameters() { return parameters; } + private JsonObject validEip4844EthTransactionParameters() { + final JsonObject parameters = validEip1559EthTransactionParameters(); + parameters.put("maxFeePerBlobGas", "0x123456789"); + parameters.put( + "versionedHashes", + List.of( + "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675")); + return parameters; + } + private JsonRpcRequest wrapParametersInRequest(final T parameters) { final JsonObject input = new JsonObject(); input.put("jsonrpc", 2.0); diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java index b37d61814..eefdc26c8 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java @@ -23,14 +23,18 @@ import java.math.BigInteger; import java.nio.ByteBuffer; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.SignedRawTransaction; import org.web3j.crypto.TransactionDecoder; import org.web3j.crypto.transaction.type.Transaction1559; +import org.web3j.crypto.transaction.type.Transaction4844; import org.web3j.crypto.transaction.type.TransactionType; import org.web3j.utils.Numeric; @@ -126,6 +130,59 @@ private static byte[] prependEip1559TransactionType(byte[] bytesToSign) { .array(); } + @Test + public void rlpEncodesEip4844Transaction() { + final EthSendTransactionJsonParameters params = + new EthSendTransactionJsonParameters("0x7577919ae5df4941180eac211965f275cdce314d"); + params.receiver("0xd46e8dd67c5d32be8058bb8eb970870f07244567"); + params.gas("0x76c0"); + params.maxPriorityFeePerGas("0x9184e72a000"); + params.maxFeePerGas("0x9184e72a001"); + params.nonce("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2"); + params.value("0x0"); + params.data( + "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"); + params.maxFeePerBlobGas("0x123456789"); + ArrayList versionedHashes = new ArrayList<>(); + versionedHashes.add("0x0102030000000000000000000000000000000000000000000000000000000000"); + params.versionedHashes(versionedHashes); + + ethTransaction = + new EthTransaction(1337L, params, () -> BigInteger.ZERO, new JsonRpcRequestId(1)); + + final SignatureData signatureData = null; + final byte[] rlpEncodedBytes = ethTransaction.rlpEncode(signatureData); + final String rlpString = Numeric.toHexString(prependEip4844TransactionType(rlpEncodedBytes)); + + final Transaction4844 decodedTransaction = + (Transaction4844) TransactionDecoder.decode(rlpString).getTransaction(); + assertThat(decodedTransaction.getTo()).isEqualTo("0xd46e8dd67c5d32be8058bb8eb970870f07244567"); + assertThat(decodedTransaction.getGasLimit()).isEqualTo(Numeric.decodeQuantity("0x76c0")); + assertThat(decodedTransaction.getMaxPriorityFeePerGas()) + .isEqualTo(Numeric.decodeQuantity("0x9184e72a000")); + assertThat(decodedTransaction.getMaxFeePerGas()) + .isEqualTo(Numeric.decodeQuantity("0x9184e72a001")); + assertThat(decodedTransaction.getNonce()) + .isEqualTo( + Numeric.decodeQuantity( + "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")); + assertThat(decodedTransaction.getValue()).isEqualTo(Numeric.decodeQuantity("0x0")); + assertThat(decodedTransaction.getData()) + .isEqualTo( + "d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"); + assertThat(decodedTransaction.getMaxFeePerBlobGas()) + .isEqualTo(Numeric.decodeQuantity("0x123456789")); + assertThat(decodedTransaction.getVersionedHashes()) + .isEqualTo(versionedHashes.stream().map(Bytes::fromHexString).collect(Collectors.toList())); + } + + private static byte[] prependEip4844TransactionType(byte[] bytesToSign) { + return ByteBuffer.allocate(bytesToSign.length + 1) + .put(TransactionType.EIP4844.getRlpType()) + .put(bytesToSign) + .array(); + } + @Test @SuppressWarnings("unchecked") public void createsJsonRequest() { From 8892d003eb936d6bdeb426a7409f569e5cfa3064 Mon Sep 17 00:00:00 2001 From: Nazarii Denha Date: Tue, 15 Oct 2024 14:01:14 +0200 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae740c634..72fcd1ed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features Added - Java 21 for build and runtime. [#995](https://github.com/Consensys/web3signer/pull/995) - Electra fork support. [#1020](https://github.com/Consensys/web3signer/pull/1020) and [#1023](https://github.com/Consensys/web3signer/pull/1023) +- BlobTx/EIP4844 transaction support for `eth_signTransaction` [#1026](https://github.com/Consensys/web3signer/pull/1026) ### Bugs fixed - Override protobuf-java to 3.25.5 which is a transitive dependency from google-cloud-secretmanager. It fixes CVE-2024-7254. From 6d87db356ee9401690469d5c6e39b9c03ba6085d Mon Sep 17 00:00:00 2001 From: Nazarii Denha Date: Tue, 15 Oct 2024 18:04:29 +0200 Subject: [PATCH 3/4] generate correct rlp encoding to sign 4844 tx --- .../EthSendTransactionJsonParameters.java | 1 + .../transaction/EthTransaction.java | 47 +++++++++++++++ .../transaction/PrivateTransaction.java | 5 ++ .../transaction/Transaction.java | 1 + .../signing/TransactionSerializer.java | 17 +++++- .../transaction/EthTransactionTest.java | 57 ------------------- 6 files changed, 70 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java index df7c320d2..558b60989 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParameters.java @@ -40,6 +40,7 @@ public class EthSendTransactionJsonParameters { private BigInteger maxFeePerBlobGas; private List versionedHashes; + @JsonCreator public EthSendTransactionJsonParameters(@JsonProperty("from") final String sender) { validateNotEmpty(sender); diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java index e6b20678c..ad5cdd581 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/EthTransaction.java @@ -12,13 +12,19 @@ */ package tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.transaction; +import org.apache.tuweni.bytes.Bytes; +import org.web3j.crypto.Sign; +import org.web3j.rlp.RlpString; +import org.web3j.utils.Numeric; import tech.pegasys.web3signer.core.service.jsonrpc.EthSendTransactionJsonParameters; import tech.pegasys.web3signer.core.service.jsonrpc.JsonRpcRequest; import tech.pegasys.web3signer.core.service.jsonrpc.JsonRpcRequestId; import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.NonceProvider; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import com.google.common.base.MoreObjects; import org.jetbrains.annotations.NotNull; @@ -71,6 +77,41 @@ public byte[] rlpEncode(final SignatureData signatureData) { return RlpEncoder.encode(rlpList); } + @Override + public byte[] rlpEncodeToSign() { + if (!isEip4844()) { + return rlpEncode(null); + } + // eip4844 transaction should be rlp encoded in another way + List resultTx = new ArrayList<>(); + + resultTx.add(RlpString.create(chainId)); + resultTx.add(RlpString.create(nonce)); + resultTx.add(RlpString.create(transactionJsonParameters.maxPriorityFeePerGas().orElseThrow())); + resultTx.add(RlpString.create(transactionJsonParameters.maxFeePerGas().orElseThrow())); + resultTx.add(RlpString.create(transactionJsonParameters.gas().orElse(DEFAULT_GAS))); + + String to = transactionJsonParameters.receiver().orElse(DEFAULT_TO); + if (to.length() > 0) { + resultTx.add(RlpString.create(Numeric.hexStringToByteArray(to))); + } else { + resultTx.add(RlpString.create("")); + } + + resultTx.add(RlpString.create(transactionJsonParameters.value().orElse(DEFAULT_VALUE))); + byte[] data = Numeric.hexStringToByteArray(transactionJsonParameters.data().orElse(DEFAULT_DATA)); + resultTx.add(RlpString.create(data)); + + // access list + resultTx.add(new RlpList()); + + // Blob Transaction: max_fee_per_blob_gas and versioned_hashes + resultTx.add(RlpString.create(transactionJsonParameters.maxFeePerBlobGas().orElseThrow())); + resultTx.add(new RlpList(getRlpVersionedHashes(transactionJsonParameters.versionedHashes().orElseThrow()))); + + return RlpEncoder.encode(new RlpList(resultTx)); + } + @Override public boolean isNonceUserSpecified() { return transactionJsonParameters.nonce().isPresent(); @@ -148,4 +189,10 @@ protected RawTransaction createTransaction() { transactionJsonParameters.data().orElse(DEFAULT_DATA)); } } + + public List getRlpVersionedHashes(List versionedHashes) { + return versionedHashes.stream() + .map(hash -> RlpString.create(hash.toArray())) + .collect(Collectors.toList()); + } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java index ac853fe2c..090d25e04 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/PrivateTransaction.java @@ -64,6 +64,11 @@ public byte[] rlpEncode(final SignatureData signatureData) { return RlpEncoder.encode(rlpList); } + @Override + public byte[] rlpEncodeToSign() { + return rlpEncode(null); + } + @Override public boolean isNonceUserSpecified() { return transactionJsonParameters.nonce().isPresent(); diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java index fb4a8676a..34a3de146 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/sendtransaction/transaction/Transaction.java @@ -33,6 +33,7 @@ public interface Transaction { void updateFieldsIfRequired(); byte[] rlpEncode(SignatureData signatureData); + byte[] rlpEncodeToSign(); boolean isNonceUserSpecified(); diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java index a4f3f44eb..d9c35d371 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java @@ -24,12 +24,15 @@ import tech.pegasys.web3signer.signing.secp256k1.Signature; import java.nio.ByteBuffer; +import java.util.Base64; import org.apache.tuweni.bytes.Bytes; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.TransactionEncoder; import org.web3j.crypto.transaction.type.TransactionType; +import javax.crypto.spec.PSource; + public class TransactionSerializer { protected final SignerForIdentifier secpSigner; @@ -79,9 +82,21 @@ private static byte[] prependEip1559TransactionType(byte[] bytesToSign) { .array(); } + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + private byte[] serializeEip4844(final Transaction transaction) { - byte[] bytesToSign = transaction.rlpEncode(null); + byte[] bytesToSign = transaction.rlpEncodeToSign(); bytesToSign = prependEip4844TransactionType(bytesToSign); + System.out.println("signing eip4844 " +bytesToHex(bytesToSign)); final SignatureData signatureData = sign(transaction.sender(), bytesToSign); diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java index eefdc26c8..b37d61814 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/sendtransaction/transaction/EthTransactionTest.java @@ -23,18 +23,14 @@ import java.math.BigInteger; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; -import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.web3j.crypto.Sign.SignatureData; import org.web3j.crypto.SignedRawTransaction; import org.web3j.crypto.TransactionDecoder; import org.web3j.crypto.transaction.type.Transaction1559; -import org.web3j.crypto.transaction.type.Transaction4844; import org.web3j.crypto.transaction.type.TransactionType; import org.web3j.utils.Numeric; @@ -130,59 +126,6 @@ private static byte[] prependEip1559TransactionType(byte[] bytesToSign) { .array(); } - @Test - public void rlpEncodesEip4844Transaction() { - final EthSendTransactionJsonParameters params = - new EthSendTransactionJsonParameters("0x7577919ae5df4941180eac211965f275cdce314d"); - params.receiver("0xd46e8dd67c5d32be8058bb8eb970870f07244567"); - params.gas("0x76c0"); - params.maxPriorityFeePerGas("0x9184e72a000"); - params.maxFeePerGas("0x9184e72a001"); - params.nonce("0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2"); - params.value("0x0"); - params.data( - "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"); - params.maxFeePerBlobGas("0x123456789"); - ArrayList versionedHashes = new ArrayList<>(); - versionedHashes.add("0x0102030000000000000000000000000000000000000000000000000000000000"); - params.versionedHashes(versionedHashes); - - ethTransaction = - new EthTransaction(1337L, params, () -> BigInteger.ZERO, new JsonRpcRequestId(1)); - - final SignatureData signatureData = null; - final byte[] rlpEncodedBytes = ethTransaction.rlpEncode(signatureData); - final String rlpString = Numeric.toHexString(prependEip4844TransactionType(rlpEncodedBytes)); - - final Transaction4844 decodedTransaction = - (Transaction4844) TransactionDecoder.decode(rlpString).getTransaction(); - assertThat(decodedTransaction.getTo()).isEqualTo("0xd46e8dd67c5d32be8058bb8eb970870f07244567"); - assertThat(decodedTransaction.getGasLimit()).isEqualTo(Numeric.decodeQuantity("0x76c0")); - assertThat(decodedTransaction.getMaxPriorityFeePerGas()) - .isEqualTo(Numeric.decodeQuantity("0x9184e72a000")); - assertThat(decodedTransaction.getMaxFeePerGas()) - .isEqualTo(Numeric.decodeQuantity("0x9184e72a001")); - assertThat(decodedTransaction.getNonce()) - .isEqualTo( - Numeric.decodeQuantity( - "0xe04d296d2460cfb8472af2c5fd05b5a214109c25688d3704aed5484f9a7792f2")); - assertThat(decodedTransaction.getValue()).isEqualTo(Numeric.decodeQuantity("0x0")); - assertThat(decodedTransaction.getData()) - .isEqualTo( - "d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"); - assertThat(decodedTransaction.getMaxFeePerBlobGas()) - .isEqualTo(Numeric.decodeQuantity("0x123456789")); - assertThat(decodedTransaction.getVersionedHashes()) - .isEqualTo(versionedHashes.stream().map(Bytes::fromHexString).collect(Collectors.toList())); - } - - private static byte[] prependEip4844TransactionType(byte[] bytesToSign) { - return ByteBuffer.allocate(bytesToSign.length + 1) - .put(TransactionType.EIP4844.getRlpType()) - .put(bytesToSign) - .array(); - } - @Test @SuppressWarnings("unchecked") public void createsJsonRequest() { From a0ddb453c08eda2d104f366e6111e80493478a8e Mon Sep 17 00:00:00 2001 From: Nazarii Denha Date: Wed, 16 Oct 2024 09:07:13 +0200 Subject: [PATCH 4/4] remove unused --- .../handlers/signing/TransactionSerializer.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java index d9c35d371..49e236c18 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/handlers/signing/TransactionSerializer.java @@ -82,21 +82,9 @@ private static byte[] prependEip1559TransactionType(byte[] bytesToSign) { .array(); } - private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = HEX_ARRAY[v >>> 4]; - hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; - } - return new String(hexChars); - } - private byte[] serializeEip4844(final Transaction transaction) { byte[] bytesToSign = transaction.rlpEncodeToSign(); bytesToSign = prependEip4844TransactionType(bytesToSign); - System.out.println("signing eip4844 " +bytesToHex(bytesToSign)); final SignatureData signatureData = sign(transaction.sender(), bytesToSign);