Skip to content

Commit

Permalink
EIP 7702 first draft
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
  • Loading branch information
daniellehrner committed Jun 18, 2024
1 parent 884834f commit 9e9311c
Show file tree
Hide file tree
Showing 18 changed files with 527 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ public void maxPrioritizedTxsPerTypeConfigFile() throws IOException {
@Test
public void maxPrioritizedTxsPerTypeWrongTxType() {
internalTestFailure(
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB] (case-insensitive) but was 'WRONG_TYPE'",
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB, EIP7702] (case-insensitive) but was 'WRONG_TYPE'",
"--tx-pool-max-prioritized-by-type",
"WRONG_TYPE=1");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.datatypes;

import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;

import java.math.BigInteger;
import java.util.List;
import java.util.function.Supplier;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Suppliers;

/**
* An access list entry as defined in EIP-7702
*
* @param chainId can be either the current chain id or zero
* @param address the address from which the code will be set into the EOA account
* @param nonces the list of nonces
* @param signature the signature of the EOA account which will be used to set the code
*/
public record SetCodeAuthorization(
BigInteger chainId, Address address, List<Long> nonces, SECPSignature signature) {

private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);

/**
* Create access list entry.
*
* @param chainId can be either the current chain id or zero
* @param address the address from which the code will be set into the EOA account
* @param nonces the list of nonces
* @param v the recovery id
* @param r the r value of the signature
* @param s the s value of the signature
* @return SetCodeTransactionEntry
*/
@JsonCreator
public static SetCodeAuthorization createAccessListEntry(
@JsonProperty("chainId") final BigInteger chainId,
@JsonProperty("address") final Address address,
@JsonProperty("nonce") final List<Long> nonces,
@JsonProperty("v") final byte v,
@JsonProperty("r") final BigInteger r,
@JsonProperty("s") final BigInteger s) {
return new SetCodeAuthorization(
chainId, address, nonces, SIGNATURE_ALGORITHM.get().createSignature(r, s, v));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,7 @@ default Optional<? extends Quantity> getMaxFeePerBlobGas() {
* @return the size in bytes of the encoded transaction.
*/
int getSize();

/** Returns the set code transaction payload if this transaction is a 7702 transaction. */
Optional<List<SetCodeAuthorization>> getSetCodeTransactionPayloads();
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ public enum TransactionType {
/** Eip1559 transaction type. */
EIP1559(0x02),
/** Blob transaction type. */
BLOB(0x03);
BLOB(0x03),
/** Eip7702 transaction type. */
SET_CODE(0x04);

private static final Set<TransactionType> ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES =
Set.of(ACCESS_LIST, EIP1559, BLOB);
Expand Down Expand Up @@ -128,4 +130,13 @@ public boolean requiresChainId() {
public boolean supportsBlob() {
return this.equals(BLOB);
}

/**
* Does transaction type require code.
*
* @return the boolean
*/
public boolean requiresSetCode() {
return this.equals(SET_CODE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;

import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
Expand Down Expand Up @@ -91,6 +92,9 @@ public class TransactionCompleteResult implements TransactionResult {
@JsonInclude(JsonInclude.Include.NON_NULL)
private final List<VersionedHash> versionedHashes;

@JsonInclude(JsonInclude.Include.NON_NULL)
private final List<SetCodeAuthorization> setCodeAuthorizationList;

public TransactionCompleteResult(final TransactionWithMetadata tx) {
final Transaction transaction = tx.getTransaction();
final TransactionType transactionType = transaction.getType();
Expand Down Expand Up @@ -125,14 +129,16 @@ public TransactionCompleteResult(final TransactionWithMetadata tx) {
this.yParity = Quantity.create(transaction.getYParity());
this.v =
(transactionType == TransactionType.ACCESS_LIST
|| transactionType == TransactionType.EIP1559)
|| transactionType == TransactionType.EIP1559)
|| transactionType == TransactionType.SET_CODE
? Quantity.create(transaction.getYParity())
: null;
}
this.value = Quantity.create(transaction.getValue());
this.r = Quantity.create(transaction.getR());
this.s = Quantity.create(transaction.getS());
this.versionedHashes = transaction.getVersionedHashes().orElse(null);
this.setCodeAuthorizationList = transaction.getSetCodeTransactionPayloads().orElse(null);
}

@JsonGetter(value = "accessList")
Expand Down Expand Up @@ -246,4 +252,9 @@ public String getS() {
public List<VersionedHash> getVersionedHashes() {
return versionedHashes;
}

@JsonGetter(value = "setCodeTransactionPayloadList")
public List<SetCodeAuthorization> getSetCodeTransactionPayloadList() {
return setCodeAuthorizationList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,15 @@
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.KZGCommitment;
import org.hyperledger.besu.datatypes.KZGProof;
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
import org.hyperledger.besu.datatypes.Sha256Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.SetCodeTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
Expand Down Expand Up @@ -122,6 +124,7 @@ public class Transaction
private final Optional<List<VersionedHash>> versionedHashes;

private final Optional<BlobsWithCommitments> blobsWithCommitments;
private final Optional<List<SetCodeAuthorization>> maybeSetCodeTransactionPayloads;

public static Builder builder() {
return new Builder();
Expand Down Expand Up @@ -177,7 +180,8 @@ private Transaction(
final Address sender,
final Optional<BigInteger> chainId,
final Optional<List<VersionedHash>> versionedHashes,
final Optional<BlobsWithCommitments> blobsWithCommitments) {
final Optional<BlobsWithCommitments> blobsWithCommitments,
final Optional<List<SetCodeAuthorization>> maybeSetCodeTransactionPayloads) {

if (!forCopy) {
if (transactionType.requiresChainId()) {
Expand Down Expand Up @@ -213,6 +217,12 @@ private Transaction(
checkArgument(
maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction");
}

if (transactionType.requiresSetCode()) {
checkArgument(
maybeSetCodeTransactionPayloads.isPresent(),
"Must specify set code transaction payload for set code transaction");
}
}

this.transactionType = transactionType;
Expand All @@ -231,6 +241,7 @@ private Transaction(
this.chainId = chainId;
this.versionedHashes = versionedHashes;
this.blobsWithCommitments = blobsWithCommitments;
this.maybeSetCodeTransactionPayloads = maybeSetCodeTransactionPayloads;
}

/**
Expand Down Expand Up @@ -462,6 +473,7 @@ private Bytes32 getOrComputeSenderRecoveryHash() {
payload,
maybeAccessList,
versionedHashes.orElse(null),
maybeSetCodeTransactionPayloads,
chainId);
}
return hashNoSignature;
Expand Down Expand Up @@ -668,6 +680,11 @@ public Optional<BlobsWithCommitments> getBlobsWithCommitments() {
return blobsWithCommitments;
}

@Override
public Optional<List<SetCodeAuthorization>> getSetCodeTransactionPayloads() {
return maybeSetCodeTransactionPayloads;
}

/**
* Return the list of transaction hashes extracted from the collection of Transaction passed as
* argument
Expand All @@ -692,6 +709,7 @@ private static Bytes32 computeSenderRecoveryHash(
final Bytes payload,
final Optional<List<AccessListEntry>> accessList,
final List<VersionedHash> versionedHashes,
final Optional<List<SetCodeAuthorization>> setCodePayloads,
final Optional<BigInteger> chainId) {
if (transactionType.requiresChainId()) {
checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType);
Expand Down Expand Up @@ -736,6 +754,21 @@ private static Bytes32 computeSenderRecoveryHash(
new IllegalStateException(
"Developer error: the transaction should be guaranteed to have an access list here")),
chainId);
case SET_CODE ->
setCodePreimage(
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
value,
payload,
chainId,
accessList,
setCodePayloads.orElseThrow(
() ->
new IllegalStateException(
"Developer error: the transaction should be guaranteed to have a set code payload here")));
};
return keccak256(preimage);
}
Expand Down Expand Up @@ -873,6 +906,38 @@ private static Bytes accessListPreimage(
return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode);
}

private static Bytes setCodePreimage(
final long nonce,
final Wei maxPriorityFeePerGas,
final Wei maxFeePerGas,
final long gasLimit,
final Optional<Address> to,
final Wei value,
final Bytes payload,
final Optional<BigInteger> chainId,
final Optional<List<AccessListEntry>> accessList,
final List<SetCodeAuthorization> setCodePayloads) {
final Bytes encoded =
RLP.encode(
rlpOutput -> {
rlpOutput.startList();
eip1559PreimageFields(
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
to,
value,
payload,
chainId,
accessList,
rlpOutput);
SetCodeTransactionEncoder.encodeSetCodeInner(setCodePayloads, rlpOutput);
rlpOutput.endList();
});
return Bytes.concatenate(Bytes.of(TransactionType.BLOB.getSerializedType()), encoded);
}

@Override
public boolean equals(final Object other) {
if (!(other instanceof Transaction that)) {
Expand Down Expand Up @@ -1040,7 +1105,8 @@ public Transaction detachedCopy() {
sender,
chainId,
detachedVersionedHashes,
detachedBlobsWithCommitments);
detachedBlobsWithCommitments,
maybeSetCodeTransactionPayloads);

// copy also the computed fields, to avoid to recompute them
copiedTx.sender = this.sender;
Expand Down Expand Up @@ -1108,6 +1174,7 @@ public static class Builder {
protected Optional<BigInteger> v = Optional.empty();
protected List<VersionedHash> versionedHashes = null;
private BlobsWithCommitments blobsWithCommitments;
protected Optional<List<SetCodeAuthorization>> setCodeTransactionPayloads;

public Builder copiedFrom(final Transaction toCopy) {
this.transactionType = toCopy.transactionType;
Expand Down Expand Up @@ -1219,6 +1286,8 @@ public Builder guessType() {
transactionType = TransactionType.EIP1559;
} else if (accessList.isPresent()) {
transactionType = TransactionType.ACCESS_LIST;
} else if (setCodeTransactionPayloads.isPresent()) {
transactionType = TransactionType.SET_CODE;
} else {
transactionType = TransactionType.FRONTIER;
}
Expand Down Expand Up @@ -1248,7 +1317,8 @@ public Transaction build() {
sender,
chainId,
Optional.ofNullable(versionedHashes),
Optional.ofNullable(blobsWithCommitments));
Optional.ofNullable(blobsWithCommitments),
setCodeTransactionPayloads);
}

public Transaction signAndBuild(final KeyPair keys) {
Expand All @@ -1275,6 +1345,7 @@ SECPSignature computeSignature(final KeyPair keys) {
payload,
accessList,
versionedHashes,
setCodeTransactionPayloads,
chainId),
keys);
}
Expand All @@ -1298,5 +1369,11 @@ public Builder blobsWithCommitments(final BlobsWithCommitments blobsWithCommitme
this.blobsWithCommitments = blobsWithCommitments;
return this;
}

public Builder setCodeTransactionPayloads(
final List<SetCodeAuthorization> setCodeTransactionEntries) {
this.setCodeTransactionPayloads = Optional.ofNullable(setCodeTransactionEntries);
return this;
}
}
}
Loading

0 comments on commit 9e9311c

Please sign in to comment.