Skip to content

Adding support for EIP1559 Private Transactions #1980

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion abi/src/main/java/org/web3j/abi/TypeDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,8 @@ private static <T extends Type> T decodeDynamicParameterFromStruct(
value = decodeDynamicStruct(dynamicElementData, 0, TypeReference.create(declaredField));
} else if (DynamicArray.class.isAssignableFrom(declaredField)) {
if (parameter == null) {
throw new RuntimeException("parameter can not be null, try to use annotation @Parameterized to specify the parameter type");
throw new RuntimeException(
"parameter can not be null, try to use annotation @Parameterized to specify the parameter type");
}
value =
(T)
Expand Down
16 changes: 8 additions & 8 deletions besu/src/main/java/org/web3j/tx/PrivateTransactionManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class PrivateTransactionManager extends TransactionManager {

private final Besu besu;

private final TxSignService txSignService;
private final TxSignService privateTxSignService;
private final long chainId;

private final Base64String privateFrom;
Expand Down Expand Up @@ -74,15 +74,15 @@ public PrivateTransactionManager(
final Base64String privateFrom,
final Base64String privacyGroupId,
final Restriction restriction,
final TxSignService txSignService) {
final TxSignService privateTxSignService) {
super(transactionReceiptProcessor, credentials.getAddress());
this.besu = besu;
this.chainId = chainId;
this.privateFrom = privateFrom;
this.privateFor = null;
this.privacyGroupId = privacyGroupId;
this.restriction = restriction;
this.txSignService = txSignService;
this.privateTxSignService = privateTxSignService;
}

public PrivateTransactionManager(
Expand Down Expand Up @@ -112,15 +112,15 @@ public PrivateTransactionManager(
final Base64String privateFrom,
final List<Base64String> privateFor,
final Restriction restriction,
final TxSignService txSignService) {
final TxSignService privateTxSignService) {
super(transactionReceiptProcessor, credentials.getAddress());
this.besu = besu;
this.chainId = chainId;
this.privateFrom = privateFrom;
this.privateFor = privateFor;
this.privacyGroupId = PrivacyGroupUtils.generateLegacyGroup(privateFrom, privateFor);
this.restriction = restriction;
this.txSignService = txSignService;
this.privateTxSignService = privateTxSignService;
}

@Override
Expand All @@ -134,7 +134,7 @@ public EthSendTransaction sendTransaction(
throws IOException {

final BigInteger nonce =
besu.privGetTransactionCount(txSignService.getAddress(), privacyGroupId)
besu.privGetTransactionCount(privateTxSignService.getAddress(), privacyGroupId)
.send()
.getTransactionCount();

Expand Down Expand Up @@ -179,7 +179,7 @@ public EthSendTransaction sendEIP1559Transaction(
boolean constructor)
throws IOException {
final BigInteger nonce =
besu.privGetTransactionCount(txSignService.getAddress(), privacyGroupId)
besu.privGetTransactionCount(privateTxSignService.getAddress(), privacyGroupId)
.send()
.getTransactionCount();

Expand Down Expand Up @@ -241,7 +241,7 @@ public EthGetCode getCode(

public String sign(final RawPrivateTransaction rawTransaction) {

final byte[] signedMessage = txSignService.sign(rawTransaction, chainId);
final byte[] signedMessage = privateTxSignService.sign(rawTransaction, chainId);

return Numeric.toHexString(signedMessage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
*/
package org.web3j.protocol.eea.crypto;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.SignedRawTransaction;
import org.web3j.crypto.TransactionDecoder;
import org.web3j.crypto.transaction.type.TransactionType;
import org.web3j.rlp.RlpDecoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
Expand All @@ -32,23 +35,111 @@ public class PrivateTransactionDecoder {

public static RawPrivateTransaction decode(final String hexTransaction) {
final byte[] transaction = Numeric.hexStringToByteArray(hexTransaction);
final TransactionType transactionType = getPrivateTransactionType(transaction);

if (transactionType == TransactionType.EIP1559) {
return decodePrivateTransaction1559(transaction);
}
return decodeLegacyPrivateTransaction(transaction);
}

private static TransactionType getPrivateTransactionType(final byte[] transaction) {
// Determine the type of the private transaction, similar to TransactionDecoder.
byte firstByte = transaction[0];
if (firstByte == TransactionType.EIP1559.getRlpType()) return TransactionType.EIP1559;
else return TransactionType.LEGACY;
}

private static RawPrivateTransaction 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<RlpType> values = temp.getValues();

final long chainId =
((RlpString) temp.getValues().get(0)).asPositiveBigInteger().longValue();
final BigInteger nonce = ((RlpString) temp.getValues().get(1)).asPositiveBigInteger();

final BigInteger maxPriorityFeePerGas =
((RlpString) temp.getValues().get(2)).asPositiveBigInteger();
final BigInteger maxFeePerGas =
((RlpString) temp.getValues().get(3)).asPositiveBigInteger();

final BigInteger gasLimit = ((RlpString) temp.getValues().get(4)).asPositiveBigInteger();
final String to = ((RlpString) temp.getValues().get(5)).asString();
final String data = ((RlpString) temp.getValues().get(7)).asString();

final Base64String privateFrom = extractBase64(values.get(8));
final Restriction restriction = extractRestriction(values.get(10));

if (values.get(9) instanceof RlpList) {
List<Base64String> privateForList = extractBase64List(values.get(9));
return RawPrivateTransaction.createTransaction(
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
data,
privateFrom,
privateForList,
null,
restriction);
} else {
Base64String privacyGroupId = extractBase64(values.get(9));
return RawPrivateTransaction.createTransaction(
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
data,
privateFrom,
null,
privacyGroupId,
restriction);
}
}

private static RawPrivateTransaction decodeLegacyPrivateTransaction(final byte[] transaction) {
final RlpList rlpList = RlpDecoder.decode(transaction);
final RlpList temp = (RlpList) rlpList.getValues().get(0);
final List<RlpType> values = temp.getValues();

final RawTransaction rawTransaction = TransactionDecoder.decode(hexTransaction);
final RawTransaction rawTransaction =
TransactionDecoder.decode(Numeric.toHexString(transaction));

if (values.size() == 9) {
final Base64String privateFrom = extractBase64(values.get(6));
final Restriction restriction = extractRestriction(values.get(8));

if (values.get(7) instanceof RlpList) {
return new RawPrivateTransaction(
rawTransaction, privateFrom, extractBase64List(values.get(7)), restriction);
List<Base64String> privateForList = extractBase64List(values.get(7));
return RawPrivateTransaction.createTransaction(
rawTransaction.getNonce(),
rawTransaction.getGasPrice(),
rawTransaction.getGasLimit(),
rawTransaction.getTo(),
rawTransaction.getData(),
privateFrom,
privateForList,
null,
restriction);
} else {
return new RawPrivateTransaction(
rawTransaction, privateFrom, extractBase64(values.get(7)), restriction);
Base64String privacyGroupId = extractBase64(values.get(7));
return RawPrivateTransaction.createTransaction(
rawTransaction.getNonce(),
rawTransaction.getGasPrice(),
rawTransaction.getGasLimit(),
rawTransaction.getTo(),
rawTransaction.getData(),
privateFrom,
null,
privacyGroupId,
restriction);
}

} else {
final Base64String privateFrom = extractBase64(values.get(9));
final Restriction restriction = extractRestriction(values.get(11));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,25 @@
package org.web3j.protocol.eea.crypto;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import org.web3j.crypto.Credentials;
import org.web3j.crypto.Sign;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.rlp.RlpEncoder;
import org.web3j.rlp.RlpList;
import org.web3j.rlp.RlpString;
import org.web3j.rlp.RlpType;
import org.web3j.utils.Base64String;

/** Create signed RLP encoded private transaction. */
public class PrivateTransactionEncoder {

public static byte[] signMessage(
final RawPrivateTransaction rawTransaction, final Credentials credentials) {
final byte[] encodedTransaction = encode(rawTransaction);
final RawPrivateTransaction 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(
Expand All @@ -61,35 +58,25 @@ public static byte[] encode(final RawPrivateTransaction rawTransaction, final lo
}

private static byte[] encode(
final RawPrivateTransaction rawTransaction, final Sign.SignatureData signatureData) {
final List<RlpType> values = asRlpValues(rawTransaction, signatureData);
final RawPrivateTransaction privateTransaction,
final Sign.SignatureData signatureData) {
final List<RlpType> values =
privateTransaction.getPrivateTransaction().asRlpValues(signatureData);
final RlpList rlpList = new RlpList(values);
return RlpEncoder.encode(rlpList);
byte[] encoded = RlpEncoder.encode(rlpList);

if (privateTransaction.getType().isEip1559()) {
return ByteBuffer.allocate(encoded.length + 1)
.put(privateTransaction.getType().getRlpType())
.put(encoded)
.array();
}
return encoded;
}

private static byte[] longToBytes(long x) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(x);
return buffer.array();
}

public static List<RlpType> asRlpValues(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any issue with this being removed for backwards compatibility?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So every Tx type has its own different asRlpValues(), so it is being transferred from here.

final RawPrivateTransaction privateTransaction,
final Sign.SignatureData signatureData) {

final List<RlpType> result =
new ArrayList<>(TransactionEncoder.asRlpValues(privateTransaction, signatureData));

result.add(privateTransaction.getPrivateFrom().asRlp());

privateTransaction
.getPrivateFor()
.ifPresent(privateFor -> result.add(Base64String.unwrapListToRlp(privateFor)));

privateTransaction.getPrivacyGroupId().map(Base64String::asRlp).ifPresent(result::add);

result.add(RlpString.create(privateTransaction.getRestriction().getRestriction()));

return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,25 @@ public PrivateTxSignServiceImpl(Credentials credentials) {
this.credentials = credentials;
}

@Override
public byte[] sign(RawTransaction rawTransaction, long chainId) {
if (!(rawTransaction instanceof RawPrivateTransaction)) {
throw new RuntimeException("Can only sign RawPrivateTransaction");
public byte[] sign(RawTransaction privateTransaction, long chainId) {
if (!(privateTransaction instanceof RawPrivateTransaction)) {
throw new RuntimeException("Can only sign LegacyPrivateTransaction");
}

final byte[] signedMessage;

if (chainId > ChainId.NONE) {
signedMessage =
PrivateTransactionEncoder.signMessage(
(RawPrivateTransaction) rawTransaction, chainId, credentials);
(RawPrivateTransaction) privateTransaction, chainId, credentials);
} else {
signedMessage =
PrivateTransactionEncoder.signMessage(
(RawPrivateTransaction) rawTransaction, credentials);
(RawPrivateTransaction) privateTransaction, credentials);
}
return signedMessage;
}

@Override
public String getAddress() {
return credentials.getAddress();
}
Expand Down
Loading