From d47d0a5211768155414954a3bd5de30308754818 Mon Sep 17 00:00:00 2001 From: Nischal Sharma Date: Mon, 6 Nov 2023 14:30:43 +0530 Subject: [PATCH] added privateTx EIP1559 Signed-off-by: Nischal Sharma --- .../web3j/tx/PrivateTransactionManager.java | 76 ++++++++--------- .../tx/PrivateTransactionManagerTest.java | 3 + .../eea/crypto/PrivateTransactionDecoder.java | 84 +++++++++++++++++-- .../eea/crypto/PrivateTransactionEncoder.java | 21 +++-- .../type/PrivateTransaction1559.java | 15 ++-- .../type/PrivateTransactionType.java | 22 +++-- .../crypto/PrivateTransactionDecoderTest.java | 42 ++++++++++ .../crypto/PrivateTransactionEncoderTest.java | 31 +++++++ 8 files changed, 227 insertions(+), 67 deletions(-) diff --git a/besu/src/main/java/org/web3j/tx/PrivateTransactionManager.java b/besu/src/main/java/org/web3j/tx/PrivateTransactionManager.java index 87e2d38755..407c21244c 100644 --- a/besu/src/main/java/org/web3j/tx/PrivateTransactionManager.java +++ b/besu/src/main/java/org/web3j/tx/PrivateTransactionManager.java @@ -26,7 +26,7 @@ import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.protocol.eea.crypto.PrivateTxSignServiceImpl; import org.web3j.protocol.eea.crypto.transaction.type.LegacyPrivateTransaction; -import org.web3j.service.TxSignService; +import org.web3j.protocol.eea.crypto.transaction.type.PrivateTransaction1559; import org.web3j.tx.response.TransactionReceiptProcessor; import org.web3j.utils.Base64String; import org.web3j.utils.Numeric; @@ -37,7 +37,7 @@ public class PrivateTransactionManager extends TransactionManager { private final Besu besu; - private final TxSignService txSignService; + private final PrivateTxSignServiceImpl privateTxSignService; private final long chainId; private final Base64String privateFrom; @@ -74,7 +74,7 @@ public PrivateTransactionManager( final Base64String privateFrom, final Base64String privacyGroupId, final Restriction restriction, - final TxSignService txSignService) { + final PrivateTxSignServiceImpl privateTxSignService) { super(transactionReceiptProcessor, credentials.getAddress()); this.besu = besu; this.chainId = chainId; @@ -82,7 +82,7 @@ public PrivateTransactionManager( this.privateFor = null; this.privacyGroupId = privacyGroupId; this.restriction = restriction; - this.txSignService = txSignService; + this.privateTxSignService = privateTxSignService; } public PrivateTransactionManager( @@ -112,7 +112,7 @@ public PrivateTransactionManager( final Base64String privateFrom, final List privateFor, final Restriction restriction, - final TxSignService txSignService) { + final PrivateTxSignServiceImpl privateTxSignService) { super(transactionReceiptProcessor, credentials.getAddress()); this.besu = besu; this.chainId = chainId; @@ -120,7 +120,7 @@ public PrivateTransactionManager( this.privateFor = privateFor; this.privacyGroupId = PrivacyGroupUtils.generateLegacyGroup(privateFrom, privateFor); this.restriction = restriction; - this.txSignService = txSignService; + this.privateTxSignService = privateTxSignService; } @Override @@ -134,7 +134,7 @@ public EthSendTransaction sendTransaction( throws IOException { final BigInteger nonce = - besu.privGetTransactionCount(txSignService.getAddress(), privacyGroupId) + besu.privGetTransactionCount(privateTxSignService.getAddress(), privacyGroupId) .send() .getTransactionCount(); @@ -179,40 +179,40 @@ public EthSendTransaction sendEIP1559Transaction( boolean constructor) throws IOException { final BigInteger nonce = - besu.privGetTransactionCount(txSignService.getAddress(), privacyGroupId) + besu.privGetTransactionCount(privateTxSignService.getAddress(), privacyGroupId) .send() .getTransactionCount(); - final LegacyPrivateTransaction transaction; - // if (privateFor != null) { - // transaction = - // RawPrivateTransaction.createTransaction( - // chainId, - // nonce, - // maxPriorityFeePerGas, - // maxFeePerGas, - // gasLimit, - // to, - // data, - // privateFrom, - // privateFor, - // restriction); - // } else { - // transaction = - // RawPrivateTransaction.createTransaction( - // chainId, - // nonce, - // maxPriorityFeePerGas, - // maxFeePerGas, - // gasLimit, - // to, - // data, - // privateFrom, - // privacyGroupId, - // restriction); - // } + final PrivateTransaction1559 transaction; + if (privateFor != null) { + transaction = + new PrivateTransaction1559( + chainId, + nonce, + gasLimit, + to, + data, + maxPriorityFeePerGas, + maxFeePerGas, + privateFrom, + privateFor, + restriction); + } else { + transaction = + new PrivateTransaction1559( + chainId, + nonce, + gasLimit, + to, + data, + maxPriorityFeePerGas, + maxFeePerGas, + privateFrom, + privacyGroupId, + restriction); + } - return signAndSend(null); + return signAndSend(transaction); } @Override @@ -241,7 +241,7 @@ public EthGetCode getCode( public String sign(final LegacyPrivateTransaction rawTransaction) { - final byte[] signedMessage = txSignService.sign(rawTransaction, chainId); + final byte[] signedMessage = privateTxSignService.sign(rawTransaction, chainId); return Numeric.toHexString(signedMessage); } diff --git a/besu/src/test/java/org/web3j/tx/PrivateTransactionManagerTest.java b/besu/src/test/java/org/web3j/tx/PrivateTransactionManagerTest.java index 415cb772b9..2f1e8d6242 100644 --- a/besu/src/test/java/org/web3j/tx/PrivateTransactionManagerTest.java +++ b/besu/src/test/java/org/web3j/tx/PrivateTransactionManagerTest.java @@ -21,6 +21,7 @@ import org.web3j.protocol.besu.Besu; import org.web3j.protocol.core.DefaultBlockParameter; import org.web3j.protocol.core.methods.response.EthCall; +import org.web3j.protocol.core.methods.response.EthSendTransaction; import org.web3j.tx.exceptions.ContractCallException; import org.web3j.tx.response.PollingPrivateTransactionReceiptProcessor; import org.web3j.tx.response.TransactionReceiptProcessor; @@ -51,6 +52,8 @@ class PrivateTransactionManagerTest { DefaultBlockParameter defaultBlockParameter = mock(DefaultBlockParameter.class); EthCall response = mock(EthCall.class); + EthSendTransaction sendTransaction = mock(EthSendTransaction.class); + @Test public void sendPrivCallTest() throws IOException { when(response.getValue()).thenReturn("test"); diff --git a/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoder.java b/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoder.java index 7826857d98..325771cd7e 100644 --- a/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoder.java +++ b/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoder.java @@ -12,6 +12,8 @@ */ package org.web3j.protocol.eea.crypto; +import java.math.BigInteger; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -19,6 +21,7 @@ import org.web3j.crypto.SignedRawTransaction; import org.web3j.crypto.TransactionDecoder; import org.web3j.protocol.eea.crypto.transaction.type.LegacyPrivateTransaction; +import org.web3j.protocol.eea.crypto.transaction.type.PrivateTransaction1559; import org.web3j.protocol.eea.crypto.transaction.type.PrivateTransactionType; import org.web3j.rlp.RlpDecoder; import org.web3j.rlp.RlpList; @@ -36,22 +39,85 @@ public static LegacyPrivateTransaction decode(final String hexTransaction) { final byte[] transaction = Numeric.hexStringToByteArray(hexTransaction); final PrivateTransactionType transactionType = getPrivateTransactionType(transaction); - switch (transactionType) { - case PRIVATE_LEGACY: - return decodeLegacyPrivateTransaction(transaction); - default: - throw new IllegalArgumentException("Unsupported private transaction type."); + if (transactionType == PrivateTransactionType.PRIVATE_1559) { + return decodePrivateTransaction1559(transaction); } + return decodeLegacyPrivateTransaction(transaction); } private static PrivateTransactionType getPrivateTransactionType(final byte[] transaction) { // Determine the type of the private transaction, similar to TransactionDecoder. byte firstByte = transaction[0]; - // if (firstByte == PrivateTransactionType.PRIVATE_LEGACY.getRlpType()) - // return PrivateTransactionType.PRIVATE_LEGACY; + if (firstByte == PrivateTransactionType.PRIVATE_1559.getRlpType()) + return PrivateTransactionType.PRIVATE_1559; + else return PrivateTransactionType.PRIVATE_LEGACY; + } + + private static LegacyPrivateTransaction decodePrivateTransaction1559(final byte[] transaction) { + final byte[] encodedTx = Arrays.copyOfRange(transaction, 1, transaction.length); + final RlpList rlpList = RlpDecoder.decode(encodedTx); + final RlpList temp = (RlpList) rlpList.getValues().get(0); + final List values = temp.getValues(); + + System.out.println(rlpList); + System.out.println(temp); + System.out.println(values); + + final long chainId = + ((RlpString) temp.getValues().get(0)).asPositiveBigInteger().longValue(); + System.out.println("chainId " + chainId); + final BigInteger nonce = ((RlpString) temp.getValues().get(1)).asPositiveBigInteger(); + System.out.println("nonce " + nonce); - // Return LEGACY as default, or throw an exception depending on your needs - return PrivateTransactionType.PRIVATE_LEGACY; + final BigInteger maxPriorityFeePerGas = + ((RlpString) temp.getValues().get(2)).asPositiveBigInteger(); + System.out.println("maxPriorityFeePerGas " + maxPriorityFeePerGas); + final BigInteger maxFeePerGas = + ((RlpString) temp.getValues().get(3)).asPositiveBigInteger(); + System.out.println("maxFeePerGas " + maxFeePerGas); + + final BigInteger gasLimit = ((RlpString) temp.getValues().get(4)).asPositiveBigInteger(); + System.out.println("gasLimit " + gasLimit); + final String to = ((RlpString) temp.getValues().get(5)).asString(); + System.out.println("to " + to); + + final String data = ((RlpString) temp.getValues().get(7)).asString(); + System.out.println("data " + data); + + final Base64String privateFrom = extractBase64(values.get(8)); + System.out.println("privateFrom " + privateFrom); + final Restriction restriction = extractRestriction(values.get(10)); + System.out.println("restriction " + restriction); + + if (values.get(9) instanceof RlpList) { + List privateForList = extractBase64List(values.get(9)); + return new PrivateTransaction1559( + chainId, + nonce, + gasLimit, + to, + data, + maxPriorityFeePerGas, + maxFeePerGas, + privateFrom, + privateForList, + null, + restriction); + } else { + Base64String privacyGroupId = extractBase64(values.get(9)); + return new PrivateTransaction1559( + chainId, + nonce, + gasLimit, + to, + data, + maxPriorityFeePerGas, + maxFeePerGas, + privateFrom, + null, + privacyGroupId, + restriction); + } } private static LegacyPrivateTransaction decodeLegacyPrivateTransaction( diff --git a/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoder.java b/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoder.java index 175414e324..7f8308dad9 100644 --- a/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoder.java +++ b/eea/src/main/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoder.java @@ -27,12 +27,12 @@ public class PrivateTransactionEncoder { public static byte[] signMessage( - final LegacyPrivateTransaction rawTransaction, final Credentials credentials) { - final byte[] encodedTransaction = encode(rawTransaction); + final LegacyPrivateTransaction privateTransaction, final Credentials credentials) { + final byte[] encodedTransaction = encode(privateTransaction); final Sign.SignatureData signatureData = Sign.signMessage(encodedTransaction, credentials.getEcKeyPair()); - return encode(rawTransaction, signatureData); + return encode(privateTransaction, signatureData); } public static byte[] signMessage( @@ -59,11 +59,20 @@ public static byte[] encode(final LegacyPrivateTransaction rawTransaction, final } private static byte[] encode( - final LegacyPrivateTransaction rawTransaction, final Sign.SignatureData signatureData) { + final LegacyPrivateTransaction privateTransaction, + final Sign.SignatureData signatureData) { // final List values = asRlpValues(rawTransaction, signatureData); - final List values = rawTransaction.asRlpValues(signatureData); + final List values = privateTransaction.asRlpValues(signatureData); final RlpList rlpList = new RlpList(values); - return RlpEncoder.encode(rlpList); + byte[] encoded = RlpEncoder.encode(rlpList); + + if (privateTransaction.getTransactionType().isPrivateEip1559()) { + return ByteBuffer.allocate(encoded.length + 1) + .put(privateTransaction.getTransactionType().getRlpType()) + .put(encoded) + .array(); + } + return encoded; } private static byte[] longToBytes(long x) { diff --git a/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransaction1559.java b/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransaction1559.java index 80a47cd09a..b5260c865c 100644 --- a/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransaction1559.java +++ b/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransaction1559.java @@ -18,7 +18,6 @@ import org.web3j.crypto.Sign; import org.web3j.crypto.transaction.type.ITransaction; -import org.web3j.rlp.RlpList; import org.web3j.rlp.RlpString; import org.web3j.rlp.RlpType; import org.web3j.utils.Base64String; @@ -32,7 +31,7 @@ public class PrivateTransaction1559 extends LegacyPrivateTransaction implements private BigInteger maxPriorityFeePerGas; private BigInteger maxFeePerGas; - protected PrivateTransaction1559( + public PrivateTransaction1559( long chainId, BigInteger nonce, BigInteger gasLimit, @@ -59,7 +58,7 @@ protected PrivateTransaction1559( this.maxFeePerGas = maxFeePerGas; } - protected PrivateTransaction1559( + public PrivateTransaction1559( long chainId, BigInteger nonce, BigInteger gasLimit, @@ -84,7 +83,7 @@ protected PrivateTransaction1559( restriction); } - protected PrivateTransaction1559( + public PrivateTransaction1559( long chainId, BigInteger nonce, BigInteger gasLimit, @@ -140,9 +139,6 @@ public List asRlpValues(final Sign.SignatureData signatureData) { byte[] data = Numeric.hexStringToByteArray(getData()); result.add(RlpString.create(data)); - // access list - result.add(new RlpList()); - if (signatureData != null) { result.add(RlpString.create(Sign.getRecId(signatureData, getChainId()))); result.add(RlpString.create(Bytes.trimLeadingZeroes(signatureData.getR()))); @@ -177,4 +173,9 @@ public BigInteger getMaxPriorityFeePerGas() { public BigInteger getMaxFeePerGas() { return maxFeePerGas; } + + @Override + public PrivateTransactionType getTransactionType() { + return PrivateTransactionType.PRIVATE_1559; + } } diff --git a/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransactionType.java b/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransactionType.java index 7e47e9d5fe..0f70d74de4 100644 --- a/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransactionType.java +++ b/eea/src/main/java/org/web3j/protocol/eea/crypto/transaction/type/PrivateTransactionType.java @@ -13,16 +13,24 @@ package org.web3j.protocol.eea.crypto.transaction.type; public enum PrivateTransactionType { - PRIVATE_LEGACY("Legacy"), - PRIVATE_1559("EIP-1559"); + PRIVATE_LEGACY(null), + PRIVATE_1559(((byte) 0x01)); - private String description; + Byte type; - PrivateTransactionType(String description) { - this.description = description; + PrivateTransactionType(final Byte type) { + this.type = type; } - public String getDescription() { - return description; + public Byte getRlpType() { + return type; + } + + public boolean isPrivateLegacy() { + return this.equals(PrivateTransactionType.PRIVATE_LEGACY); + } + + public boolean isPrivateEip1559() { + return this.equals(PrivateTransactionType.PRIVATE_1559); } } diff --git a/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoderTest.java b/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoderTest.java index 0e90954d25..7e5cf28539 100644 --- a/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoderTest.java +++ b/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionDecoderTest.java @@ -21,6 +21,7 @@ import org.web3j.crypto.Credentials; import org.web3j.crypto.Sign; import org.web3j.protocol.eea.crypto.transaction.type.LegacyPrivateTransaction; +import org.web3j.protocol.eea.crypto.transaction.type.PrivateTransaction1559; import org.web3j.utils.Base64String; import org.web3j.utils.Numeric; @@ -68,6 +69,47 @@ public void testDecoding() { assertEquals(RESTRICTED, result.getRestriction()); } + @Test + public void testDecoding1559() { + final BigInteger nonce = BigInteger.ZERO; + final long chainId = 2018; + final BigInteger gasLimit = BigInteger.TEN; + final BigInteger maxPriorityFeePerGas = BigInteger.ONE; + final BigInteger maxFeePerGas = BigInteger.ONE; + final String to = "0x0add5355"; + + final PrivateTransaction1559 privateTx = + new PrivateTransaction1559( + chainId, + nonce, + gasLimit, + to, + "", + maxPriorityFeePerGas, + maxFeePerGas, + MOCK_ENCLAVE_KEY, + MOCK_PRIVATE_FOR, + null, + RESTRICTED); + + byte[] encodedMessage = PrivateTransactionEncoder.encode(privateTx); + final String hexMessage = Numeric.toHexString(encodedMessage); + + final PrivateTransaction1559 result = + (PrivateTransaction1559) PrivateTransactionDecoder.decode(hexMessage); + assertNotNull(result); + assertEquals(nonce, result.getNonce()); + assertEquals(chainId, result.getChainId()); + assertEquals(maxPriorityFeePerGas, result.getMaxPriorityFeePerGas()); + assertEquals(maxFeePerGas, result.getMaxFeePerGas()); + assertEquals(gasLimit, result.getGasLimit()); + assertEquals(to, result.getTo()); + assertEquals("", result.getData()); + assertEquals(MOCK_ENCLAVE_KEY, result.getPrivateFrom()); + assertEquals(MOCK_PRIVATE_FOR, result.getPrivateFor().get()); + assertEquals(RESTRICTED, result.getRestriction()); + } + @Test public void testDecodingPrivacyGroup() { final BigInteger nonce = BigInteger.ZERO; diff --git a/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoderTest.java b/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoderTest.java index 1646d237cd..f57e673d9e 100644 --- a/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoderTest.java +++ b/eea/src/test/java/org/web3j/protocol/eea/crypto/PrivateTransactionEncoderTest.java @@ -20,6 +20,7 @@ import org.web3j.crypto.Credentials; import org.web3j.protocol.eea.crypto.transaction.type.LegacyPrivateTransaction; +import org.web3j.protocol.eea.crypto.transaction.type.PrivateTransaction1559; import org.web3j.utils.Base64String; import org.web3j.utils.Numeric; @@ -64,6 +65,36 @@ public void testSignLegacyTransaction() { assertEquals(expected, privateRawTransaction); } + @Test + public void testSign1559Transaction() { + final String expected = + "0x01f8d48207e2800101832dc6c094627306090abab3a6e1400e9345bc60c78a8bef57808001a0c0e9d5c7656fdf74a5b5423b20c01663e6c3ee9f855a2371a0bbb07569fafd6ea02543caba35757c6b9401eebfe724c538cb94f817106927fedaa4669ffcbae7efa0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486af842a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486aa02a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b8a72657374726963746564"; + final long chainId = 2018; + final PrivateTransaction1559 privateTransactionCreation = + new PrivateTransaction1559( + chainId, + BigInteger.ZERO, + BigInteger.valueOf(3000000), + "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "0x", + BigInteger.ONE, + BigInteger.ONE, + MOCK_ENCLAVE_KEY, + MOCK_PRIVATE_FOR, + null, + RESTRICTED); + + final String privateKey = + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63"; + final Credentials credentials = Credentials.create(privateKey); + final String privateRawTransaction = + Numeric.toHexString( + PrivateTransactionEncoder.signMessage( + privateTransactionCreation, credentials)); + + assertEquals(expected, privateRawTransaction); + } + @Test public void testSignBesuTransaction() { final String expected =