mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 23:08:15 -05:00
Pectra devnet 5: 7702 changes (#8118)
* first draft for 7702 changes for pectra-devnet5 Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * spotless, javadoc Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fixed get code in call operations Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * allow code delegation chain id to be up to 2^256-1 Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * set code for delegated accounts correctly in the initialFrame of the tx processing Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * delegated accounts return empty bytes for the code if the target does not exist Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> --------- Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
This commit is contained in:
@@ -16,7 +16,6 @@ package org.hyperledger.besu.evm.account;
|
||||
|
||||
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;
|
||||
|
||||
@@ -24,14 +23,14 @@ 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) {
|
||||
@@ -45,7 +44,8 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @return the delegated code.
|
||||
*/
|
||||
protected Bytes getCode() {
|
||||
@Override
|
||||
public Optional<Bytes> getDelegatedCode() {
|
||||
return resolveDelegatedCode();
|
||||
}
|
||||
|
||||
@@ -54,27 +54,9 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,19 +64,27 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @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());
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ import org.hyperledger.besu.datatypes.Wei;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* A world state account.
|
||||
*
|
||||
@@ -71,13 +69,4 @@ public interface Account extends AccountState {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Hash;
|
||||
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;
|
||||
@@ -74,6 +75,15 @@ 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.
|
||||
*
|
||||
@@ -81,6 +91,15 @@ public interface AccountState {
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.apache.tuweni.bytes.Bytes32;
|
||||
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;
|
||||
|
||||
@@ -81,17 +81,12 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
|
||||
@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
|
||||
@@ -106,7 +101,7 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
return wrappedAccount.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,9 +114,4 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
final Bytes32 startKeyHash, final int limit) {
|
||||
return wrappedAccount.storageEntriesFrom(startKeyHash, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.apache.tuweni.bytes.Bytes32;
|
||||
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;
|
||||
@@ -83,17 +83,12 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
|
||||
@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
|
||||
@@ -108,7 +103,7 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
return wrappedAccount.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,9 +151,4 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
public void becomeImmutable() {
|
||||
wrappedAccount.becomeImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -192,13 +191,24 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
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(
|
||||
delegatedCodeResolutionGas, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
}
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress());
|
||||
@@ -218,10 +228,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
|
||||
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()) {
|
||||
@@ -337,4 +344,23 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,12 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -127,13 +125,24 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
Address to = Words.toAddress(toBytes);
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator, contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
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(
|
||||
delegatedCodeResolutionGas, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
}
|
||||
|
||||
boolean accountCreation = (contract == null || contract.isEmpty()) && !zeroValue;
|
||||
@@ -154,10 +163,7 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
currentGas -= cost;
|
||||
frame.expandMemory(inputOffset, inputLength);
|
||||
|
||||
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()) {
|
||||
|
||||
@@ -16,7 +16,6 @@ package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
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.evm.EVM;
|
||||
@@ -26,7 +25,7 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -96,16 +95,7 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
|
||||
final Bytes code = getCode(account);
|
||||
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
@@ -118,4 +108,14 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (account == null) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
return account.hasDelegatedCode()
|
||||
? DelegateCodeHelper.getDelegatedCodeForRead()
|
||||
: account.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
@@ -27,7 +25,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -85,19 +83,10 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
if (account == null || account.isEmpty()) {
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
final Bytes code = getCode(account);
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
@@ -115,4 +104,12 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (!account.hasDelegatedCode()) {
|
||||
return account.getCode();
|
||||
}
|
||||
|
||||
return DelegateCodeHelper.getDelegatedCodeForRead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
@@ -26,7 +24,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -82,20 +80,11 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
} else {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
Bytes codeSize;
|
||||
if (account == null) {
|
||||
codeSize = Bytes.EMPTY;
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
final Bytes code = getCode(account);
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
@@ -114,4 +103,14 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (account == null) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
return account.hasDelegatedCode()
|
||||
? DelegateCodeHelper.getDelegatedCodeForRead()
|
||||
: account.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.evm.worldstate;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** Helper class for 7702 delegated code interactions */
|
||||
public class DelegateCodeHelper {
|
||||
/**
|
||||
* The designator that is returned when a ExtCode* operation calls a contract with delegated code
|
||||
*/
|
||||
public static final Bytes DELEGATED_CODE_DESIGNATOR = Bytes.fromHexString("ef01");
|
||||
|
||||
/** The prefix that is used to identify delegated code */
|
||||
public static final Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
|
||||
|
||||
/** The size of the delegated code */
|
||||
public static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
|
||||
|
||||
/** create a new DelegateCodeHelper */
|
||||
public DelegateCodeHelper() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the provided code is delegated code.
|
||||
*
|
||||
* @param code the code to check.
|
||||
* @return {@code true} if the code is delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean hasDelegatedCode(final Bytes code) {
|
||||
return code != null
|
||||
&& code.size() == DELEGATED_CODE_SIZE
|
||||
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delegated code designator
|
||||
*
|
||||
* @return the hardcoded designator for delegated code: ef01
|
||||
*/
|
||||
public static Bytes getDelegatedCodeForRead() {
|
||||
return DELEGATED_CODE_DESIGNATOR;
|
||||
}
|
||||
}
|
||||
@@ -33,22 +33,6 @@ public class DelegatedCodeGasCostHelper {
|
||||
// empty constructor
|
||||
}
|
||||
|
||||
/** The status of the operation. */
|
||||
public enum Status {
|
||||
/** The operation failed due to insufficient gas. */
|
||||
INSUFFICIENT_GAS,
|
||||
/** The operation was successful. */
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of the operation.
|
||||
*
|
||||
* @param gasCost the gas cost
|
||||
* @param status of the operation
|
||||
*/
|
||||
public record Result(long gasCost, Status status) {}
|
||||
|
||||
/**
|
||||
* Deducts the gas cost for delegated code resolution.
|
||||
*
|
||||
@@ -57,26 +41,18 @@ public class DelegatedCodeGasCostHelper {
|
||||
* @param account the account
|
||||
* @return the gas cost and result of the operation
|
||||
*/
|
||||
public static Result deductDelegatedCodeGasCost(
|
||||
public static long delegatedCodeGasCost(
|
||||
final MessageFrame frame, final GasCalculator gasCalculator, final Account account) {
|
||||
if (!account.hasDelegatedCode()) {
|
||||
return new Result(0, Status.SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (account.delegatedCodeAddress().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have a delegated code address");
|
||||
}
|
||||
|
||||
final long delegatedCodeResolutionGas =
|
||||
calculateDelegatedCodeResolutionGas(
|
||||
frame, gasCalculator, account.delegatedCodeAddress().get());
|
||||
|
||||
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
|
||||
return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
return new Result(delegatedCodeResolutionGas, Status.SUCCESS);
|
||||
return calculateDelegatedCodeResolutionGas(
|
||||
frame, gasCalculator, account.delegatedCodeAddress().get());
|
||||
}
|
||||
|
||||
private static long calculateDelegatedCodeResolutionGas(
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.worldstate;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.DELEGATED_CODE_PREFIX;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.hasDelegatedCode;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.DelegatedCodeAccount;
|
||||
@@ -25,8 +28,6 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** A service that manages the code injection of delegated code. */
|
||||
public class DelegatedCodeService {
|
||||
private static final Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
|
||||
private static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
|
||||
|
||||
private final GasCalculator gasCalculator;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class DelegatedCodeService {
|
||||
* @return {@code true} if the account can set delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public boolean canSetDelegatedCode(final Account account) {
|
||||
return account.getCode().isEmpty() || hasDelegatedCode(account.getUnprocessedCode());
|
||||
return account.getCode().isEmpty() || hasDelegatedCode(account.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,18 +103,6 @@ public class DelegatedCodeService {
|
||||
worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the provided code is delegated code.
|
||||
*
|
||||
* @param code the code to check.
|
||||
* @return {@code true} if the code is delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean hasDelegatedCode(final Bytes code) {
|
||||
return code != null
|
||||
&& code.size() == DELEGATED_CODE_SIZE
|
||||
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX);
|
||||
}
|
||||
|
||||
private Address resolveDelegatedAddress(final Bytes code) {
|
||||
return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user