Skip to content

Commit

Permalink
7702 for devenet-3 (hyperledger#7444)
Browse files Browse the repository at this point in the history
* wrapped WorldUpdater into `EVMWorldupdater` to remove the authority code injection from the implementation of the actual world updaters

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* add CHANGELOG entry

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* first draft for 7702 v2

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* change return value of DelegatedCodeGasCostHelper

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fix upfront gas cost calculation, fix setting code multiple times in MutableDelegatedCodeAccount

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fix gas refund for delegated code when account already exists, added gas cost deduction for code delegation resolution to ExtCodeSizeOperation

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* allow accounts with delegated code to send transactions

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* add refund for already existing account after nonce check

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* resolve delegated code only the first time to avoid delegation loops

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* handle invalid authorization signatures properly

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* refactored CodeDelegationProcessor to compute authorizer of a code delegation after the chain id check

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fix canSetDelegatedCode method by checking code how it is in the trie and not the resolved code

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* optimize code hash calculation for empty code, fix check for empty code delegation list

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* check the all code delegation signatures hava a valid s value

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* rename encoder & decoder, handle invalid signature values in T8nExecutor

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* create the signatures for code delegation authorizations in T8nExecutor without checking if they are valid to test them later during the tx execution

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* check that recid is either 0 or 1

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fixed acceptance tests, renamed the the remaining instances of set code to code delegation

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fix delegate encoder & encoder unit tests

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* spotless

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* changed empty check for delegated accounts, fixed test

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* fix hasDelegatedCode method when code is null

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* run acceptance tests without deamon

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* disable CodeDelegationTransactionAcceptanceTest to check if it is causing the stuck ci pipeline

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* check if shouldTransferAllEthOfAuthorizerToSponsor is causing pipeline to stall

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* check if shouldCheckNonceAfterNonceIncreaseOfSender is causing pipeline to stall

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* check if closing the cluster after every test is causing pipeline to stall

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

* spotless

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>

---------

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Danno Ferrin <danno@numisight.com>
  • Loading branch information
daniellehrner and shemnon authored Sep 6, 2024
1 parent cf592c4 commit 8eee569
Show file tree
Hide file tree
Showing 44 changed files with 999 additions and 568 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@

import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.CodeDelegation;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
Expand All @@ -39,7 +39,7 @@
import org.junit.jupiter.api.Test;
import org.web3j.protocol.core.methods.response.TransactionReceipt;

public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
public class CodeDelegationTransactionAcceptanceTest extends AcceptanceTestBase {
private static final String GENESIS_FILE = "/dev/dev_prague.json";
private static final SECP256K1 secp256k1 = new SECP256K1();

Expand Down Expand Up @@ -74,7 +74,6 @@ void setUp() throws IOException {
@AfterEach
void tearDown() {
besuNode.close();
cluster.close();
}

/**
Expand All @@ -88,17 +87,18 @@ void tearDown() {
public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {

// 7702 transaction
final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization =
SetCodeAuthorization.builder()
final CodeDelegation authorization =
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
.chainId(BigInteger.valueOf(20211))
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
.nonce(0)
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));

final Transaction tx =
Transaction.builder()
.type(TransactionType.SET_CODE)
.type(TransactionType.DELEGATE_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1000000000))
Expand All @@ -108,7 +108,7 @@ public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {
.value(Wei.ZERO)
.payload(Bytes32.leftPad(Bytes.fromHexString(transactionSponsor.getAddress())))
.accessList(List.of())
.setCodeTransactionPayloads(List.of(authorization))
.codeDelegations(List.of(authorization))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(
Expand All @@ -134,40 +134,39 @@ public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {

/**
* The authorizer creates an authorization for a contract that sends all its ETH to any given
* address. But the nonce is 1 and the authorization list is processed before the nonce increase
* of the sender. Therefore, the authorization should be invalid and will be ignored. No balance
* change, except for a decrease for paying the transaction cost should occur.
* address. The nonce is 1 and the authorization list is processed after the nonce increase of the
* sender. Therefore, the authorization should be valid. The authorizer balance should be 0 and
* the transaction sponsor's * balance should be 180000 ETH minus the transaction costs.
*/
@Test
public void shouldCheckNonceBeforeNonceIncreaseOfSender() throws IOException {

public void shouldCheckNonceAfterNonceIncreaseOfSender() throws IOException {
final long GAS_LIMIT = 1000000L;
cluster.verify(authorizer.balanceEquals(Amount.ether(90000)));

final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization =
SetCodeAuthorization.builder()
final CodeDelegation authorization =
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
.chainId(BigInteger.valueOf(20211))
.nonces(
Optional.of(
1L)) // nonce is 1, but because it is validated before the nonce increase, it
// should be 0
.nonce(
1L) // nonce is 1, but because it is validated before the nonce increase, it should
// be 0
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));

final Transaction tx =
Transaction.builder()
.type(TransactionType.SET_CODE)
.type(TransactionType.DELEGATE_CODE)
.chainId(BigInteger.valueOf(20211))
.nonce(0)
.maxPriorityFeePerGas(Wei.of(1000000000))
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
.gasLimit(1000000)
.gasLimit(GAS_LIMIT)
.to(Address.fromHexStringStrict(authorizer.getAddress()))
.value(Wei.ZERO)
.payload(Bytes32.leftPad(Bytes.fromHexString(otherAccount.getAddress())))
.accessList(List.of())
.setCodeTransactionPayloads(List.of(authorization))
.codeDelegations(List.of(authorization))
.signAndBuild(
secp256k1.createKeyPair(
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
Expand All @@ -180,14 +179,25 @@ public void shouldCheckNonceBeforeNonceIncreaseOfSender() throws IOException {
besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
assertThat(maybeTransactionReceipt).isPresent();

// verify that the balance of the other account has not changed
cluster.verify(otherAccount.balanceEquals(0));

final String gasPriceWithout0x =
maybeTransactionReceipt.get().getEffectiveGasPrice().substring(2);
final BigInteger txCost =
maybeTransactionReceipt.get().getGasUsed().multiply(new BigInteger(gasPriceWithout0x, 16));
BigInteger expectedSenderBalance = new BigInteger("90000000000000000000000").subtract(txCost);
cluster.verify(authorizer.balanceEquals(Amount.wei(expectedSenderBalance)));
final BigInteger gasPrice = new BigInteger(gasPriceWithout0x, 16);
final BigInteger txCost = maybeTransactionReceipt.get().getGasUsed().multiply(gasPrice);

final BigInteger authorizerBalance = besuNode.execute(ethTransactions.getBalance(authorizer));

// The remaining balance of the authorizer should the gas limit multiplied by the gas price
// minus the transaction cost.
// The following executes this calculation in reverse.
assertThat(GAS_LIMIT).isEqualTo(authorizerBalance.add(txCost).divide(gasPrice).longValue());

// The other accounts balance should be the initial 9000 ETH balance from the authorizer minus
// the remaining balance of the authorizer and minus the transaction cost
cluster.verify(
otherAccount.balanceEquals(
Amount.wei(
new BigInteger("90000000000000000000000")
.subtract(authorizerBalance)
.subtract(txCost))));
}
}
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, SET_CODE] (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, DELEGATE_CODE] (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
Expand Up @@ -54,7 +54,7 @@ public class SECPSignature {
* @param s the s
* @param recId the rec id
*/
SECPSignature(final BigInteger r, final BigInteger s, final byte recId) {
public SECPSignature(final BigInteger r, final BigInteger s, final byte recId) {
this.r = r;
this.s = s;
this.recId = recId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
import java.util.Optional;

/**
* SetCodeAuthorization is a data structure that represents the authorization to set code on a EOA
* account.
* CodeDelegation is a data structure that represents the authorization to delegate code of an EOA
* account to another account.
*/
public interface SetCodeAuthorization {
public interface CodeDelegation {
/** The cost of delegating code on an existing account. */
long PER_AUTH_BASE_COST = 2_500L;

/**
* Return the chain id.
*
Expand Down Expand Up @@ -53,11 +56,11 @@ public interface SetCodeAuthorization {
Optional<Address> authorizer();

/**
* Return a valid nonce or empty otherwise. A nonce is valid if the size of the list is exactly 1
* Return the nonce
*
* @return all the optional nonce
* @return the nonce
*/
Optional<Long> nonce();
long nonce();

/**
* Return the recovery id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,16 @@ default Optional<? extends Quantity> getMaxFeePerBlobGas() {
int getSize();

/**
* Returns the set code transaction payload if this transaction is a 7702 transaction.
* Returns the code delegations if this transaction is a 7702 transaction.
*
* @return the set code transaction payloads
* @return the code delegations
*/
Optional<List<SetCodeAuthorization>> getAuthorizationList();
Optional<List<CodeDelegation>> getCodeDelegationList();

/**
* Returns the size of the authorization list.
*
* @return the size of the authorization list
*/
int authorizationListSize();
int codeDelegationListSize();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public enum TransactionType {
/** Blob transaction type. */
BLOB(0x03),
/** Eip7702 transaction type. */
SET_CODE(0x04);
DELEGATE_CODE(0x04);

private static final Set<TransactionType> ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES =
Set.of(ACCESS_LIST, EIP1559, BLOB, SET_CODE);
Set.of(ACCESS_LIST, EIP1559, BLOB, DELEGATE_CODE);

private static final EnumSet<TransactionType> LEGACY_FEE_MARKET_TRANSACTION_TYPES =
EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST);
Expand Down Expand Up @@ -86,7 +86,7 @@ public static TransactionType of(final int serializedTypeValue) {
TransactionType.ACCESS_LIST,
TransactionType.EIP1559,
TransactionType.BLOB,
TransactionType.SET_CODE
TransactionType.DELEGATE_CODE
})
.filter(transactionType -> transactionType.typeValue == serializedTypeValue)
.findFirst()
Expand Down Expand Up @@ -132,12 +132,21 @@ public boolean supportsBlob() {
return this.equals(BLOB);
}

/**
* Does transaction type support delegate code.
*
* @return the boolean
*/
public boolean supportsDelegateCode() {
return this.equals(DELEGATE_CODE);
}

/**
* Does transaction type require code.
*
* @return the boolean
*/
public boolean requiresSetCode() {
return this.equals(SET_CODE);
public boolean requiresCodeDelegation() {
return this.equals(DELEGATE_CODE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +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.CodeDelegation;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
Expand Down Expand Up @@ -94,7 +94,7 @@ public class TransactionCompleteResult implements TransactionResult {
private final List<VersionedHash> versionedHashes;

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

public TransactionCompleteResult(final TransactionWithMetadata tx) {
final Transaction transaction = tx.getTransaction();
Expand Down Expand Up @@ -131,15 +131,15 @@ public TransactionCompleteResult(final TransactionWithMetadata tx) {
this.v =
(transactionType == TransactionType.ACCESS_LIST
|| transactionType == TransactionType.EIP1559)
|| transactionType == TransactionType.SET_CODE
|| transactionType == TransactionType.DELEGATE_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.authorizationList = transaction.getAuthorizationList().orElse(null);
this.authorizationList = transaction.getCodeDelegationList().orElse(null);
}

@JsonGetter(value = "accessList")
Expand Down Expand Up @@ -255,7 +255,7 @@ public List<VersionedHash> getVersionedHashes() {
}

@JsonGetter(value = "authorizationList")
public List<SetCodeAuthorization> getAuthorizationList() {
public List<CodeDelegation> getAuthorizationList() {
return authorizationList;
}
}
Loading

0 comments on commit 8eee569

Please sign in to comment.