Skip to content
Merged
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
Copy Markdown
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
Copy Markdown
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