Skip to content

Commit

Permalink
Pectra devnet 5: 7702 changes (#8118)
Browse files Browse the repository at this point in the history
* first draft for 7702 changes for pectra-devnet5

Signed-off-by: Daniel Lehrner <[email protected]>

* spotless, javadoc

Signed-off-by: Daniel Lehrner <[email protected]>

* fixed get code in call operations

Signed-off-by: Daniel Lehrner <[email protected]>

* allow code delegation chain id to be up to 2^256-1

Signed-off-by: Daniel Lehrner <[email protected]>

* set code for delegated accounts correctly in the initialFrame of the tx processing

Signed-off-by: Daniel Lehrner <[email protected]>

* delegated accounts return empty bytes for the code if the target does not exist

Signed-off-by: Daniel Lehrner <[email protected]>

---------

Signed-off-by: Daniel Lehrner <[email protected]>
Co-authored-by: Simon Dudley <[email protected]>
  • Loading branch information
daniellehrner and siladu authored Jan 16, 2025
1 parent 479bec0 commit b5fdcc0
Show file tree
Hide file tree
Showing 15 changed files with 213 additions and 177 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,16 @@ public TransactionProcessingResult processTransaction(
.inputData(transaction.getPayload())
.code(
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.map(
c -> {
if (c.hasDelegatedCode()) {
return messageCallProcessor.getCodeFromEVM(
c.getDelegatedCodeHash().get(), c.getDelegatedCode().get());
}

return messageCallProcessor.getCodeFromEVM(
c.getCodeHash(), c.getCode());
})
.orElse(CodeV0.EMPTY_CODE))
.accessListWarmAddresses(warmAddressList)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;

import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.hasDelegatedCode;

import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
Expand All @@ -32,7 +33,6 @@
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeService;

import java.math.BigInteger;
import java.util.List;
Expand All @@ -52,8 +52,6 @@
*/
public class MainnetTransactionValidator implements TransactionValidator {

public static final BigInteger TWO_POW_8 = BigInteger.TWO.pow(8);
public static final BigInteger TWO_POW_64 = BigInteger.TWO.pow(64);
public static final BigInteger TWO_POW_256 = BigInteger.TWO.pow(256);

private final GasCalculator gasCalculator;
Expand Down Expand Up @@ -166,9 +164,9 @@ private static ValidationResult<TransactionInvalidReason> validateCodeDelegation
.map(
codeDelegations -> {
for (CodeDelegation codeDelegation : codeDelegations) {
if (codeDelegation.chainId().compareTo(TWO_POW_64) >= 0) {
if (codeDelegation.chainId().compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException(
"Invalid 'chainId' value, should be < 2^64 but got "
"Invalid 'chainId' value, should be < 2^256 but got "
+ codeDelegation.chainId());
}

Expand Down Expand Up @@ -329,8 +327,7 @@ public ValidationResult<TransactionInvalidReason> validateForSender(
}

private static boolean canSendTransaction(final Account sender, final Hash codeHash) {
return codeHash.equals(Hash.EMPTY)
|| DelegatedCodeService.hasDelegatedCode(sender.getUnprocessedCode());
return codeHash.equals(Hash.EMPTY) || hasDelegatedCode(sender.getCode());
}

private ValidationResult<TransactionInvalidReason> validateTransactionSignature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,21 @@

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;

class BaseDelegatedCodeAccount {
abstract class AbstractDelegatedCodeAccount implements Account {
private final WorldUpdater worldUpdater;
private final GasCalculator gasCalculator;

/** The address of the account that has delegated code to be loaded into it. */
protected final Address delegatedCodeAddress;

protected BaseDelegatedCodeAccount(
protected AbstractDelegatedCodeAccount(
final WorldUpdater worldUpdater,
final Address delegatedCodeAddress,
final GasCalculator gasCalculator) {
Expand All @@ -45,7 +44,8 @@ protected BaseDelegatedCodeAccount(
*
* @return the delegated code.
*/
protected Bytes getCode() {
@Override
public Optional<Bytes> getDelegatedCode() {
return resolveDelegatedCode();
}

Expand All @@ -54,47 +54,37 @@ protected Bytes getCode() {
*
* @return the hash of the delegated code.
*/
protected Hash getCodeHash() {
final Bytes code = getCode();
return (code == null || code.isEmpty()) ? Hash.EMPTY : Hash.hash(code);
}

/**
* Returns the balance of the delegated account.
*
* @return the balance of the delegated account.
*/
protected Wei getDelegatedBalance() {
return getDelegatedAccount().map(Account::getBalance).orElse(Wei.ZERO);
}

/**
* Returns the nonce of the delegated account.
*
* @return the nonce of the delegated account.
*/
protected long getDelegatedNonce() {
return getDelegatedAccount().map(Account::getNonce).orElse(Account.DEFAULT_NONCE);
@Override
public Optional<Hash> getDelegatedCodeHash() {
return getDelegatedCode().map(Hash::hash);
}

/**
* Returns the address of the delegated code.
*
* @return the address of the delegated code.
*/
protected Optional<Address> delegatedCodeAddress() {
@Override
public Optional<Address> delegatedCodeAddress() {
return Optional.of(delegatedCodeAddress);
}

@Override
public boolean hasDelegatedCode() {
return true;
}

private Optional<Account> getDelegatedAccount() {
return Optional.ofNullable(worldUpdater.getAccount(delegatedCodeAddress));
}

private Bytes resolveDelegatedCode() {
if (gasCalculator.isPrecompile(delegatedCodeAddress)) {
return Bytes.EMPTY;
private Optional<Bytes> resolveDelegatedCode() {
final Optional<Account> maybeDelegatedAccount = getDelegatedAccount();

if (gasCalculator.isPrecompile(delegatedCodeAddress) || maybeDelegatedAccount.isEmpty()) {
return Optional.of(Bytes.EMPTY);
}

return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(Bytes.EMPTY);
return Optional.of(maybeDelegatedAccount.get().getCode());
}
}
11 changes: 0 additions & 11 deletions evm/src/main/java/org/hyperledger/besu/evm/account/Account.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@

import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;

/**
* A world state account.
*
Expand Down Expand Up @@ -71,13 +69,4 @@ default Optional<Address> delegatedCodeAddress() {
default boolean hasDelegatedCode() {
return false;
}

/**
* Returns the code as it is stored in the trie even if it's a delegated code account.
*
* @return the code as it is stored in the trie.
*/
default Bytes getUnprocessedCode() {
return getCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.hyperledger.besu.datatypes.Wei;

import java.util.NavigableMap;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
Expand Down Expand Up @@ -74,13 +75,31 @@ public interface AccountState {
*/
Bytes getCode();

/**
* The optional EVM bytecode if the account has set a 7702 code delegation.
*
* @return the delegated code (which may be empty).
*/
default Optional<Bytes> getDelegatedCode() {
return Optional.empty();
}

/**
* The hash of the EVM bytecode associated with this account.
*
* @return the hash of the account code (which may be {@link Hash#EMPTY}).
*/
Hash getCodeHash();

/**
* The optional hash of the delegated EVM bytecode if the account has set a 7702 code delegation.
*
* @return the hash of the delegated code (which may be empty).
*/
default Optional<Hash> getDelegatedCodeHash() {
return Optional.empty();
}

/**
* Whether the account has (non empty) EVM bytecode associated to it.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import org.apache.tuweni.units.bigints.UInt256;

/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Account {
public class DelegatedCodeAccount extends AbstractDelegatedCodeAccount implements Account {

private final Account wrappedAccount;

Expand Down Expand Up @@ -81,17 +81,12 @@ public Wei getBalance() {

@Override
public Bytes getCode() {
return super.getCode();
}

@Override
public Bytes getUnprocessedCode() {
return wrappedAccount.getCode();
}

@Override
public Hash getCodeHash() {
return super.getCodeHash();
return wrappedAccount.getCodeHash();
}

@Override
Expand All @@ -106,7 +101,7 @@ public UInt256 getOriginalStorageValue(final UInt256 key) {

@Override
public boolean isEmpty() {
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
return wrappedAccount.isEmpty();
}

@Override
Expand All @@ -119,9 +114,4 @@ public NavigableMap<Bytes32, AccountStorageEntry> storageEntriesFrom(
final Bytes32 startKeyHash, final int limit) {
return wrappedAccount.storageEntriesFrom(startKeyHash, limit);
}

@Override
public boolean hasDelegatedCode() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import org.apache.tuweni.units.bigints.UInt256;

/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
public class MutableDelegatedCodeAccount extends AbstractDelegatedCodeAccount
implements MutableAccount {

private final MutableAccount wrappedAccount;
Expand Down Expand Up @@ -83,17 +83,12 @@ public Wei getBalance() {

@Override
public Bytes getCode() {
return super.getCode();
}

@Override
public Bytes getUnprocessedCode() {
return wrappedAccount.getCode();
}

@Override
public Hash getCodeHash() {
return super.getCodeHash();
return wrappedAccount.getCodeHash();
}

@Override
Expand All @@ -108,7 +103,7 @@ public UInt256 getOriginalStorageValue(final UInt256 key) {

@Override
public boolean isEmpty() {
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
return wrappedAccount.isEmpty();
}

@Override
Expand Down Expand Up @@ -156,9 +151,4 @@ public Map<UInt256, UInt256> getUpdatedStorage() {
public void becomeImmutable() {
wrappedAccount.becomeImmutable();
}

@Override
public boolean hasDelegatedCode() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
package org.hyperledger.besu.evm.operation;

import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
Expand Down Expand Up @@ -192,13 +191,24 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {

final Account contract = frame.getWorldUpdater().get(to);

if (contract != null) {
final DelegatedCodeGasCostHelper.Result result =
deductDelegatedCodeGasCost(frame, gasCalculator(), contract);
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
if (contract != null && contract.hasDelegatedCode()) {
if (contract.getDelegatedCode().isEmpty()) {
throw new RuntimeException("A delegated code account must have delegated code");
}

if (contract.getDelegatedCodeHash().isEmpty()) {
throw new RuntimeException("A delegated code account must have a delegated code hash");
}

final long delegatedCodeResolutionGas =
DelegatedCodeGasCostHelper.delegatedCodeGasCost(frame, gasCalculator(), contract);

if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
return new Operation.OperationResult(
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
delegatedCodeResolutionGas, ExceptionalHaltReason.INSUFFICIENT_GAS);
}

frame.decrementRemainingGas(delegatedCodeResolutionGas);
}

final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress());
Expand All @@ -218,10 +228,7 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {

final Bytes inputData = frame.readMutableMemory(inputDataOffset(frame), inputDataLength(frame));

final Code code =
contract == null
? CodeV0.EMPTY_CODE
: evm.getCode(contract.getCodeHash(), contract.getCode());
final Code code = getCode(evm, contract);

// invalid code results in a quick exit
if (!code.isValid()) {
Expand Down Expand Up @@ -337,4 +344,23 @@ Bytes getCallResultStackItem(final MessageFrame childFrame) {
return LEGACY_FAILURE_STACK_ITEM;
}
}

/**
* Gets the code from the contract or EOA with delegated code.
*
* @param evm the evm
* @param account the account which needs to be retrieved
* @return the code
*/
protected static Code getCode(final EVM evm, final Account account) {
if (account == null) {
return CodeV0.EMPTY_CODE;
}

if (account.hasDelegatedCode()) {
return evm.getCode(account.getDelegatedCodeHash().get(), account.getDelegatedCode().get());
}

return evm.getCode(account.getCodeHash(), account.getCode());
}
}
Loading

0 comments on commit b5fdcc0

Please sign in to comment.