EIP-7702: devnet-4 changes (#7809)

* warm up to address at tx start if account is delegated, restrict auth nonce to 2**64-1

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

* rename requestsRoot to requestsHash

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* return no code if account has delegated code to precompile, treat precompile always as warm account when resolving code

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

* make accessListWarmAddresses generic again

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

* warm delegatee account if transaction destination has delegated code

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

* * verify auth nonce less than 2**64-1 during auth processing
* auth to zero address deletes delegation
* auth to precompile returns empty code
* auth nonce < 2**8
* increase auth base cost to 12500

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

* generalised requests flat encoding and engine api changes

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* javadoc

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* get tests passing

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* get tests passing

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* clean code

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* change requests to single requestData for each requestType

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* fix PoWBlockCreatorTest after requests data type change

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* don't return request type in getPayload result

Signed-off-by: Jason Frame <jason.frame@consensys.net>

don't return request type in getPayload result

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* include requests in t8n response

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* update contract addresses for consolidation requests and withdrawal requests

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* fix requestHash calculation

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* Ensure that execution requests always return a response

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* added and fixed bound checks, fixed some compilation errors after the rebase

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

* revert changes to evm tool spec tests

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* clean up

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* replace AbstractSystemCallRequestProcessor to concrete class and remove specific processors

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* spotless

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* update evmtool tests for 7685 changes

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* use empty requests hash prague fork at genesis

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* review suggestions

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* temporarily comment out osakaTime from Prague

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

* engine API validation

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* update plugin API hash

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* fix GenesisStateTest

Signed-off-by: Jason Frame <jason.frame@consensys.net>

* comment out unused evmWorldUpdater.parentUpdater() check

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

* added CodeDelegationProcessorTest

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

* code clean up

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>
Signed-off-by: Jason Frame <jason.frame@consensys.net>
Co-authored-by: Jason Frame <jason.frame@consensys.net>
pull/7951/merge
daniellehrner 1 day ago committed by GitHub
parent 747a378017
commit 07637cfc12
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java
  2. 9
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java
  3. 2
      crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java
  4. 27
      crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java
  5. 2
      datatypes/src/main/java/org/hyperledger/besu/datatypes/CodeDelegation.java
  6. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java
  7. 1
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java
  8. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/CodeDelegation.java
  9. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  10. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java
  11. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoder.java
  12. 2
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java
  13. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessor.java
  14. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  15. 12
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  16. 22
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  17. 8
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoderTest.java
  18. 236
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessorTest.java
  19. 2
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java
  20. 12
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java
  21. 10
      evm/src/main/java/org/hyperledger/besu/evm/account/BaseDelegatedCodeAccount.java
  22. 7
      evm/src/main/java/org/hyperledger/besu/evm/account/DelegatedCodeAccount.java
  23. 7
      evm/src/main/java/org/hyperledger/besu/evm/account/MutableDelegatedCodeAccount.java
  24. 10
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java
  25. 5
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java
  26. 14
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeGasCostHelper.java
  27. 31
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java
  28. 11
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/EVMWorldUpdater.java

@ -214,7 +214,7 @@ public abstract class AbstractSECP256 implements SignatureAlgorithm {
@Override
public CodeDelegationSignature createCodeDelegationSignature(
final BigInteger r, final BigInteger s, final BigInteger yParity) {
final BigInteger r, final BigInteger s, final byte yParity) {
return CodeDelegationSignature.create(r, s, yParity);
}

@ -42,7 +42,7 @@ public class CodeDelegationSignature extends SECPSignature {
* @return the new CodeDelegationSignature
*/
public static CodeDelegationSignature create(
final BigInteger r, final BigInteger s, final BigInteger yParity) {
final BigInteger r, final BigInteger s, final byte yParity) {
checkNotNull(r);
checkNotNull(s);
@ -56,11 +56,6 @@ public class CodeDelegationSignature extends SECPSignature {
"Invalid 's' value, should be < 2^256 but got " + s.toString(16));
}
if (yParity.compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException(
"Invalid 'yParity' value, should be < 2^256 but got " + yParity.toString(16));
}
return new CodeDelegationSignature(r, s, yParity.byteValue());
return new CodeDelegationSignature(r, s, yParity);
}
}

@ -224,7 +224,7 @@ public interface SignatureAlgorithm {
* @return the code delegation signature
*/
CodeDelegationSignature createCodeDelegationSignature(
final BigInteger r, final BigInteger s, final BigInteger yParity);
final BigInteger r, final BigInteger s, final byte yParity);
/**
* Decode secp signature.

@ -29,19 +29,19 @@ class CodeDelegationSignatureTest {
void testValidInputs() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ONE;
byte yParity = (byte) 1;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS());
assertThat(yParity.byteValue()).isEqualTo(result.getRecId());
assertThat(yParity).isEqualTo(result.getRecId());
}
@Test
void testNullRValue() {
BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO;
byte yParity = (byte) 0;
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(null, s, yParity));
@ -50,7 +50,7 @@ class CodeDelegationSignatureTest {
@Test
void testNullSValue() {
BigInteger r = BigInteger.ONE;
BigInteger yParity = BigInteger.ZERO;
byte yParity = (byte) 0;
assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, null, yParity));
@ -60,7 +60,7 @@ class CodeDelegationSignatureTest {
void testRValueExceedsTwoPow256() {
BigInteger r = TWO_POW_256;
BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO;
byte yParity = (byte) 0;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
@ -71,34 +71,23 @@ class CodeDelegationSignatureTest {
void testSValueExceedsTwoPow256() {
BigInteger r = BigInteger.ONE;
BigInteger s = TWO_POW_256;
BigInteger yParity = BigInteger.ZERO;
byte yParity = (byte) 0;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
.withMessageContainingAll("Invalid 's' value, should be < 2^256");
}
@Test
void testYParityExceedsTwoPow256() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TWO;
BigInteger yParity = TWO_POW_256;
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
.withMessageContainingAll("Invalid 'yParity' value, should be < 2^256");
}
@Test
void testValidYParityZero() {
BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO;
byte yParity = (byte) 0;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS());
assertThat(yParity.byteValue()).isEqualTo(result.getRecId());
assertThat(yParity).isEqualTo(result.getRecId());
}
}

@ -25,7 +25,7 @@ import java.util.Optional;
*/
public interface CodeDelegation {
/** The cost of delegating code on an existing account. */
long PER_AUTH_BASE_COST = 2_500L;
long PER_AUTH_BASE_COST = 12_500L;
/**
* Return the chain id.

@ -107,6 +107,7 @@ public class TracedJsonRpcProcessor implements JsonRpcProcessor {
case INVALID_PROPOSAL_PARAMS:
case INVALID_REMOTE_CAPABILITIES_PARAMS:
case INVALID_REWARD_PERCENTILES_PARAMS:
case INVALID_REQUESTS_PARAMS:
case INVALID_SEALER_ID_PARAMS:
case INVALID_STORAGE_KEYS_PARAMS:
case INVALID_SUBSCRIPTION_PARAMS:

@ -95,6 +95,7 @@ public enum RpcErrorType implements RpcMethodError {
INVALID_REMOTE_CAPABILITIES_PARAMS(
INVALID_PARAMS_ERROR_CODE, "Invalid remote capabilities params"),
INVALID_REWARD_PERCENTILES_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid reward percentiles params"),
INVALID_REQUESTS_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid requests params"),
INVALID_SEALER_ID_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid sealer ID params"),
INVALID_STORAGE_KEYS_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid storage keys params"),
INVALID_SUBSCRIPTION_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid subscription params"),

@ -20,7 +20,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationEncoder;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationTransactionEncoder;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.math.BigInteger;
@ -140,7 +140,7 @@ public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelega
private Optional<Address> computeAuthority() {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
CodeDelegationEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput);
CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput);
final Hash hash = Hash.hash(Bytes.concatenate(MAGIC, rlpOutput.encoded()));

@ -38,7 +38,7 @@ import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationEncoder;
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
@ -937,7 +937,8 @@ public class Transaction
chainId,
accessList,
rlpOutput);
CodeDelegationEncoder.encodeCodeDelegationInner(authorizationList, rlpOutput);
CodeDelegationTransactionEncoder.encodeCodeDelegationInner(
authorizationList, rlpOutput);
rlpOutput.endList();
});
return Bytes.concatenate(Bytes.of(TransactionType.DELEGATE_CODE.getSerializedType()), encoded);

@ -81,7 +81,7 @@ public class CodeDelegationTransactionDecoder {
final Address address = Address.wrap(input.readBytes());
final long nonce = input.readLongScalar();
final BigInteger yParity = input.readUInt256Scalar().toUnsignedBigInteger();
final byte yParity = (byte) input.readUnsignedByteScalar();
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger();

@ -25,9 +25,9 @@ import java.util.List;
import org.apache.tuweni.bytes.Bytes;
public class CodeDelegationEncoder {
public class CodeDelegationTransactionEncoder {
private CodeDelegationEncoder() {
private CodeDelegationTransactionEncoder() {
// private constructor
}
@ -49,7 +49,7 @@ public class CodeDelegationEncoder {
final CodeDelegation payload, final RLPOutput rlpOutput) {
rlpOutput.startList();
encodeAuthorizationDetails(payload, rlpOutput);
rlpOutput.writeIntScalar(payload.signature().getRecId());
rlpOutput.writeUnsignedByte(payload.signature().getRecId() & 0xFF);
rlpOutput.writeBigIntegerScalar(payload.signature().getR());
rlpOutput.writeBigIntegerScalar(payload.signature().getS());
rlpOutput.endList();

@ -41,7 +41,7 @@ public class TransactionEncoder {
TransactionType.BLOB,
BlobTransactionEncoder::encode,
TransactionType.DELEGATE_CODE,
CodeDelegationEncoder::encode);
CodeDelegationTransactionEncoder::encode);
private static final ImmutableMap<TransactionType, Encoder> POOLED_TRANSACTION_ENCODERS =
ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode);

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.mainnet;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.core.CodeDelegation;
import org.hyperledger.besu.ethereum.core.Transaction;
@ -30,9 +32,12 @@ public class CodeDelegationProcessor {
private static final Logger LOG = LoggerFactory.getLogger(CodeDelegationProcessor.class);
private final Optional<BigInteger> maybeChainId;
private final BigInteger halfCurveOrder;
public CodeDelegationProcessor(final Optional<BigInteger> maybeChainId) {
public CodeDelegationProcessor(
final Optional<BigInteger> maybeChainId, final BigInteger halfCurveOrder) {
this.maybeChainId = maybeChainId;
this.halfCurveOrder = halfCurveOrder;
}
/**
@ -89,6 +94,22 @@ public class CodeDelegationProcessor {
return;
}
if (codeDelegation.nonce() == MAX_NONCE) {
LOG.trace("Nonce of code delegation must be less than 2^64-1");
return;
}
if (codeDelegation.signature().getS().compareTo(halfCurveOrder) > 0) {
LOG.trace(
"Invalid signature for code delegation. S value must be less or equal than the half curve order.");
return;
}
if (codeDelegation.signature().getRecId() != 0 && codeDelegation.signature().getRecId() != 1) {
LOG.trace("Invalid signature for code delegation. RecId must be 0 or 1.");
return;
}
final Optional<Address> authorizer = codeDelegation.authorizer();
if (authorizer.isEmpty()) {
LOG.trace("Invalid signature for code delegation");
@ -128,7 +149,9 @@ public class CodeDelegationProcessor {
result.incremenentAlreadyExistingDelegators();
}
evmWorldUpdater.authorizedCodeService().addDelegatedCode(authority, codeDelegation.address());
evmWorldUpdater
.authorizedCodeService()
.processDelegatedCodeAuthorization(authority, codeDelegation.address());
authority.incrementNonce();
}
}

@ -18,6 +18,8 @@ import static org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsProc
import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.PowAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
@ -80,6 +82,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.IntStream;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.io.Resources;
import io.vertx.core.json.JsonArray;
@ -89,6 +93,9 @@ public abstract class MainnetProtocolSpecs {
private static final Address RIPEMD160_PRECOMPILE =
Address.fromHexString("0x0000000000000000000000000000000000000003");
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
// A consensus bug at Ethereum mainnet transaction 0xcf416c53
// deleted an empty account even when the message execution scope
// failed, but the transaction itself succeeded.
@ -714,7 +721,8 @@ public abstract class MainnetProtocolSpecs {
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.eip1559(),
new CodeDelegationProcessor(chainId)))
new CodeDelegationProcessor(
chainId, SIGNATURE_ALGORITHM.get().getHalfCurveOrder())))
// change to check for max blob gas per block for EIP-4844
.transactionValidatorFactoryBuilder(
(evm, gasLimitCalculator, feeMarket) ->

@ -286,7 +286,7 @@ public class MainnetTransactionProcessor {
final TransactionValidationParams transactionValidationParams,
final PrivateMetadataUpdater privateMetadataUpdater,
final Wei blobGasPrice) {
final EVMWorldUpdater evmWorldUpdater = new EVMWorldUpdater(worldState);
final EVMWorldUpdater evmWorldUpdater = new EVMWorldUpdater(worldState, gasCalculator);
try {
final var transactionValidator = transactionValidatorFactory.get();
LOG.trace("Starting execution of {}", transaction);
@ -352,6 +352,8 @@ public class MainnetTransactionProcessor {
codeDelegationRefund =
gasCalculator.calculateDelegateCodeGasRefund(
(codeDelegationResult.alreadyExistingDelegators()));
evmWorldUpdater.commit();
}
final List<AccessListEntry> accessListEntries = transaction.getAccessList().orElse(List.of());
@ -415,7 +417,6 @@ public class MainnetTransactionProcessor {
.miningBeneficiary(miningBeneficiary)
.blockHashLookup(blockHashLookup)
.contextVariables(contextVariablesBuilder.build())
.accessListWarmAddresses(warmAddressList)
.accessListWarmStorage(storageList);
if (transaction.getVersionedHashes().isPresent()) {
@ -439,11 +440,17 @@ public class MainnetTransactionProcessor {
.contract(contractAddress)
.inputData(initCodeBytes.slice(code.getSize()))
.code(code)
.accessListWarmAddresses(warmAddressList)
.build();
} else {
@SuppressWarnings("OptionalGetWithoutIsPresent") // isContractCall tests isPresent
final Address to = transaction.getTo().get();
final Optional<Account> maybeContract = Optional.ofNullable(evmWorldUpdater.get(to));
if (maybeContract.isPresent() && maybeContract.get().hasDelegatedCode()) {
warmAddressList.add(maybeContract.get().delegatedCodeAddress().get());
}
initialFrame =
commonMessageFrameBuilder
.type(MessageFrame.Type.MESSAGE_CALL)
@ -454,6 +461,7 @@ public class MainnetTransactionProcessor {
maybeContract
.map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode()))
.orElse(CodeV0.EMPTY_CODE))
.accessListWarmAddresses(warmAddressList)
.build();
}
Deque<MessageFrame> messageFrameStack = initialFrame.getMessageFrameStack();

@ -52,6 +52,8 @@ import org.bouncycastle.crypto.digests.SHA256Digest;
*/
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;
@ -158,30 +160,26 @@ public class MainnetTransactionValidator implements TransactionValidator {
"transaction code delegation transactions must have a to address");
}
final BigInteger halfCurveOrder = SignatureAlgorithmFactory.getInstance().getHalfCurveOrder();
final Optional<ValidationResult<TransactionInvalidReason>> validationResult =
transaction
.getCodeDelegationList()
.map(
codeDelegations -> {
for (CodeDelegation codeDelegation : codeDelegations) {
if (codeDelegation.chainId().compareTo(TWO_POW_256) >= 0) {
if (codeDelegation.chainId().compareTo(TWO_POW_64) >= 0) {
throw new IllegalArgumentException(
"Invalid 'chainId' value, should be < 2^256 but got "
"Invalid 'chainId' value, should be < 2^64 but got "
+ codeDelegation.chainId());
}
if (codeDelegation.signature().getS().compareTo(halfCurveOrder) > 0) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_SIGNATURE,
"Invalid signature for code delegation. S value must be less or equal than the half curve order.");
if (codeDelegation.r().compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException(
"Invalid 'r' value, should be < 2^256 but got " + codeDelegation.r());
}
if (codeDelegation.signature().getRecId() != 0
&& codeDelegation.signature().getRecId() != 1) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_SIGNATURE,
"Invalid signature for code delegation. RecId value must be 0 or 1.");
if (codeDelegation.s().compareTo(TWO_POW_256) >= 0) {
throw new IllegalArgumentException(
"Invalid 's' value, should be < 2^256 but got " + codeDelegation.s());
}
}

@ -30,7 +30,7 @@ import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class CodeDelegationEncoderTest {
class CodeDelegationTransactionEncoderTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@ -59,7 +59,7 @@ class CodeDelegationEncoderTest {
"3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16),
(byte) 0));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded())
.isEqualTo(
@ -85,7 +85,7 @@ class CodeDelegationEncoderTest {
"25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16),
(byte) 1));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded())
.isEqualTo(
@ -111,7 +111,7 @@ class CodeDelegationEncoderTest {
"3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16),
(byte) 1));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded())
.isEqualTo(

@ -0,0 +1,236 @@
/*
* 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.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.CodeDelegation;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeService;
import org.hyperledger.besu.evm.worldstate.EVMWorldUpdater;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class CodeDelegationProcessorTest {
@Mock private EVMWorldUpdater worldUpdater;
@Mock private Transaction transaction;
@Mock private DelegatedCodeService authorizedCodeService;
@Mock private MutableAccount authority;
private CodeDelegationProcessor processor;
private static final BigInteger CHAIN_ID = BigInteger.valueOf(1);
private static final BigInteger HALF_CURVE_ORDER = BigInteger.valueOf(1000);
private static final Address DELEGATE_ADDRESS =
Address.fromHexString("0x9876543210987654321098765432109876543210");
@BeforeEach
void setUp() {
processor = new CodeDelegationProcessor(Optional.of(CHAIN_ID), HALF_CURVE_ORDER);
}
@Test
void shouldRejectInvalidChainId() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(BigInteger.valueOf(2), 0L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater, never()).createAccount(any());
verify(worldUpdater, never()).getAccount(any());
}
@Test
void shouldRejectMaxNonce() {
// Arrange
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, Account.MAX_NONCE);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater, never()).createAccount(any());
verify(worldUpdater, never()).getAccount(any());
}
@Test
void shouldProcessValidDelegationForNewAccount() {
// Arrange
when(worldUpdater.authorizedCodeService()).thenReturn(authorizedCodeService);
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 0L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(null);
when(worldUpdater.createAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(0L);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(worldUpdater).createAccount(any());
verify(authority).incrementNonce();
verify(authorizedCodeService).processDelegatedCodeAuthorization(authority, DELEGATE_ADDRESS);
}
@Test
void shouldProcessValidDelegationForExistingAccount() {
// Arrange
when(worldUpdater.authorizedCodeService()).thenReturn(authorizedCodeService);
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(authority.getNonce()).thenReturn(1L);
when(authorizedCodeService.canSetDelegatedCode(any())).thenReturn(true);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isEqualTo(1);
verify(worldUpdater, never()).createAccount(any());
verify(authority).incrementNonce();
verify(authorizedCodeService).processDelegatedCodeAuthorization(authority, DELEGATE_ADDRESS);
}
@Test
void shouldRejectDelegationWithInvalidNonce() {
// Arrange
when(worldUpdater.authorizedCodeService()).thenReturn(authorizedCodeService);
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 2L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(authorizedCodeService.canSetDelegatedCode(any())).thenReturn(true);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, never()).incrementNonce();
verify(authorizedCodeService, never()).processDelegatedCodeAuthorization(any(), any());
}
@Test
void shouldRejectDelegationWithSGreaterThanHalfCurveOrder() {
// Arrange
CodeDelegation codeDelegation =
createCodeDelegation(CHAIN_ID, 1L, HALF_CURVE_ORDER.add(BigInteger.ONE));
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, never()).incrementNonce();
verify(authorizedCodeService, never()).processDelegatedCodeAuthorization(any(), any());
}
@Test
void shouldRejectDelegationWithRecIdNeitherZeroNorOne() {
// Arrange
final SECPSignature signature = new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 2);
CodeDelegation codeDelegation =
new org.hyperledger.besu.ethereum.core.CodeDelegation(
CHAIN_ID, CodeDelegationProcessorTest.DELEGATE_ADDRESS, 1L, signature);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, never()).incrementNonce();
verify(authorizedCodeService, never()).processDelegatedCodeAuthorization(any(), any());
}
@Test
void shouldRejectDelegationWithInvalidSignature() {
// Arrange
CodeDelegation codeDelegation = mock(org.hyperledger.besu.ethereum.core.CodeDelegation.class);
when(codeDelegation.chainId()).thenReturn(CHAIN_ID);
when(codeDelegation.nonce()).thenReturn(1L);
when(codeDelegation.signature())
.thenReturn(new SECPSignature(BigInteger.ONE, BigInteger.ONE, (byte) 0));
when(codeDelegation.authorizer()).thenReturn(Optional.empty());
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, never()).incrementNonce();
verify(authorizedCodeService, never()).processDelegatedCodeAuthorization(any(), any());
}
@Test
void shouldRejectDelegationWhenCannotSetDelegatedCode() {
// Arrange
when(worldUpdater.authorizedCodeService()).thenReturn(authorizedCodeService);
CodeDelegation codeDelegation = createCodeDelegation(CHAIN_ID, 1L);
when(transaction.getCodeDelegationList()).thenReturn(Optional.of(List.of(codeDelegation)));
when(worldUpdater.getAccount(any())).thenReturn(authority);
when(authorizedCodeService.canSetDelegatedCode(any())).thenReturn(false);
// Act
CodeDelegationResult result = processor.process(worldUpdater, transaction);
// Assert
assertThat(result.alreadyExistingDelegators()).isZero();
verify(authority, never()).incrementNonce();
verify(authorizedCodeService, never()).processDelegatedCodeAuthorization(any(), any());
}
private CodeDelegation createCodeDelegation(final BigInteger chainId, final long nonce) {
return createCodeDelegation(chainId, nonce, BigInteger.ONE);
}
private CodeDelegation createCodeDelegation(
final BigInteger chainId, final long nonce, final BigInteger s) {
final SECPSignature signature = new SECPSignature(BigInteger.ONE, s, (byte) 0);
return new org.hyperledger.besu.ethereum.core.CodeDelegation(
chainId, CodeDelegationProcessorTest.DELEGATE_ADDRESS, nonce, signature);
}
}

@ -89,7 +89,7 @@ class MainnetTransactionProcessorTest {
MAX_STACK_SIZE,
FeeMarket.legacy(),
CoinbaseFeePriceCalculator.frontier(),
new CodeDelegationProcessor(Optional.of(BigInteger.ONE)));
new CodeDelegationProcessor(Optional.of(BigInteger.ONE), BigInteger.TEN));
}
@Test

@ -231,10 +231,14 @@ public class T8nExecutor {
final long authorizationNonce =
Bytes.fromHexStringLenient(entryAsJson.get("nonce").textValue()).toLong();
final byte authorizationV =
final BigInteger authorizationV =
Bytes.fromHexStringLenient(entryAsJson.get("v").textValue())
.toUnsignedBigInteger()
.byteValueExact();
.toUnsignedBigInteger();
if (authorizationV.compareTo(BigInteger.valueOf(256)) >= 0) {
throw new IllegalArgumentException(
"Invalid authorizationV value. Must be less than 256");
}
final BigInteger authorizationR =
Bytes.fromHexStringLenient(entryAsJson.get("r").textValue())
.toUnsignedBigInteger();
@ -243,7 +247,7 @@ public class T8nExecutor {
.toUnsignedBigInteger();
final SECPSignature authorizationSignature =
new SECPSignature(authorizationR, authorizationS, authorizationV);
new SECPSignature(authorizationR, authorizationS, authorizationV.byteValue());
authorizations.add(
new org.hyperledger.besu.ethereum.core.CodeDelegation(

@ -17,6 +17,7 @@ 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;
import java.util.Optional;
@ -25,13 +26,17 @@ import org.apache.tuweni.bytes.Bytes;
class BaseDelegatedCodeAccount {
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(
final WorldUpdater worldUpdater, final Address delegatedCodeAddress) {
final WorldUpdater worldUpdater,
final Address delegatedCodeAddress,
final GasCalculator gasCalculator) {
this.worldUpdater = worldUpdater;
this.gasCalculator = gasCalculator;
this.delegatedCodeAddress = delegatedCodeAddress;
}
@ -86,6 +91,9 @@ class BaseDelegatedCodeAccount {
}
private Bytes resolveDelegatedCode() {
if (gasCalculator.isPrecompile(delegatedCodeAddress)) {
return Bytes.EMPTY;
}
return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(Bytes.EMPTY);
}

@ -17,6 +17,7 @@ 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;
import java.util.NavigableMap;
@ -37,12 +38,14 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
* @param worldUpdater the world updater.
* @param wrappedAccount the account that has delegated code to be loaded into it.
* @param codeDelegationAddress the address of the delegated code.
* @param gasCalculator the gas calculator to check for precompiles.
*/
public DelegatedCodeAccount(
final WorldUpdater worldUpdater,
final Account wrappedAccount,
final Address codeDelegationAddress) {
super(worldUpdater, codeDelegationAddress);
final Address codeDelegationAddress,
final GasCalculator gasCalculator) {
super(worldUpdater, codeDelegationAddress, gasCalculator);
this.wrappedAccount = wrappedAccount;
}

@ -17,6 +17,7 @@ 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;
import java.util.Map;
@ -39,12 +40,14 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
* @param worldUpdater the world updater.
* @param wrappedAccount the account that has delegated code to be loaded into it.
* @param codeDelegationAddress the address of the delegated code.
* @param gasCalculator the gas calculator to check for precompiles.
*/
public MutableDelegatedCodeAccount(
final WorldUpdater worldUpdater,
final MutableAccount wrappedAccount,
final Address codeDelegationAddress) {
super(worldUpdater, codeDelegationAddress);
final Address codeDelegationAddress,
final GasCalculator gasCalculator) {
super(worldUpdater, codeDelegationAddress, gasCalculator);
this.wrappedAccount = wrappedAccount;
}

@ -666,14 +666,4 @@ public interface GasCalculator {
default long calculateDelegateCodeGasRefund(final long alreadyExistingAccountSize) {
return 0L;
}
/**
* Returns the gas cost for resolving the code of a delegate account.
*
* @param isWarm whether the account is warm
* @return the gas cost
*/
default long delegatedCodeResolutionGasCost(final boolean isWarm) {
return 0L;
}
}

@ -52,9 +52,4 @@ public class PragueGasCalculator extends CancunGasCalculator {
public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) {
return existingAccountGasRefund * alreadyExistingAccounts;
}
@Override
public long delegatedCodeResolutionGasCost(final boolean isWarm) {
return isWarm ? getWarmStorageReadCost() : getColdAccountAccessCost();
}
}

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.evm.worldstate;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
@ -66,9 +67,9 @@ public class DelegatedCodeGasCostHelper {
throw new RuntimeException("A delegated code account must have a delegated code address");
}
final boolean delegatedCodeIsWarm = frame.warmUpAddress(account.delegatedCodeAddress().get());
final long delegatedCodeResolutionGas =
gasCalculator.delegatedCodeResolutionGasCost(delegatedCodeIsWarm);
calculateDelegatedCodeResolutionGas(
frame, gasCalculator, account.delegatedCodeAddress().get());
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS);
@ -77,4 +78,13 @@ public class DelegatedCodeGasCostHelper {
frame.decrementRemainingGas(delegatedCodeResolutionGas);
return new Result(delegatedCodeResolutionGas, Status.SUCCESS);
}
private static long calculateDelegatedCodeResolutionGas(
final MessageFrame frame, final GasCalculator gasCalculator, final Address delegateeAddress) {
final boolean delegatedCodeIsWarm =
frame.warmUpAddress(delegateeAddress) || gasCalculator.isPrecompile(delegateeAddress);
return delegatedCodeIsWarm
? gasCalculator.getWarmStorageReadCost()
: gasCalculator.getColdAccountAccessCost();
}
}

@ -19,6 +19,7 @@ import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.DelegatedCodeAccount;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.account.MutableDelegatedCodeAccount;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.apache.tuweni.bytes.Bytes;
@ -27,16 +28,32 @@ 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;
/** Creates a new DelegatedCodeService. */
public DelegatedCodeService() {}
private final GasCalculator gasCalculator;
/**
* Add the delegated code to the given account.
* Creates a new DelegatedCodeService.
*
* @param gasCalculator the gas calculator to check for pre compiles.
*/
public DelegatedCodeService(final GasCalculator gasCalculator) {
this.gasCalculator = gasCalculator;
}
/**
* Process the delegated code authorization. It will set the code to 0x ef0100 + delegated code
* address. If the address is 0, it will set the code to empty.
*
* @param account the account to which the delegated code is added.
* @param delegatedCodeAddress the address of the delegated code.
* @param delegatedCodeAddress the address of the target of the authorization.
*/
public void addDelegatedCode(final MutableAccount account, final Address delegatedCodeAddress) {
public void processDelegatedCodeAuthorization(
final MutableAccount account, final Address delegatedCodeAddress) {
// authorization to zero address removes any delegated code
if (delegatedCodeAddress.equals(Address.ZERO)) {
account.setCode(Bytes.EMPTY);
return;
}
account.setCode(Bytes.concatenate(DELEGATED_CODE_PREFIX, delegatedCodeAddress));
}
@ -64,7 +81,7 @@ public class DelegatedCodeService {
}
return new DelegatedCodeAccount(
worldUpdater, account, resolveDelegatedAddress(account.getCode()));
worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator);
}
/**
@ -82,7 +99,7 @@ public class DelegatedCodeService {
}
return new MutableDelegatedCodeAccount(
worldUpdater, account, resolveDelegatedAddress(account.getCode()));
worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator);
}
/**

@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Collection;
import java.util.Optional;
@ -35,9 +36,10 @@ public class EVMWorldUpdater implements WorldUpdater {
* Instantiates a new EVM world updater.
*
* @param rootWorldUpdater the root world updater
* @param gasCalculator the gas calculator to check for precompiles.
*/
public EVMWorldUpdater(final WorldUpdater rootWorldUpdater) {
this(rootWorldUpdater, new DelegatedCodeService());
public EVMWorldUpdater(final WorldUpdater rootWorldUpdater, final GasCalculator gasCalculator) {
this(rootWorldUpdater, new DelegatedCodeService(gasCalculator));
}
private EVMWorldUpdater(
@ -110,7 +112,10 @@ public class EVMWorldUpdater implements WorldUpdater {
@Override
public Optional<WorldUpdater> parentUpdater() {
return rootWorldUpdater.parentUpdater();
return rootWorldUpdater.parentUpdater().isPresent()
? Optional.of(
new EVMWorldUpdater(rootWorldUpdater.parentUpdater().get(), delegatedCodeService))
: Optional.empty();
}
@Override

Loading…
Cancel
Save