Skip to content
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

Pectra devnet 5: 7702 changes #8118

Merged
merged 10 commits into from
Jan 16, 2025
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());
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return Optional.of(maybeDelegatedAccount.get().getCode());
return maybeDelegatedAccount.map(AccountState::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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
protected static Code getCode(final EVM evm, final Account account) {
protected 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
Loading