From 07637cfc12908427cd5cb1b1a5d01406845e06a3 Mon Sep 17 00:00:00 2001 From: daniellehrner Date: Tue, 3 Dec 2024 11:51:43 +0100 Subject: [PATCH] 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 * rename requestsRoot to requestsHash Signed-off-by: Jason Frame * return no code if account has delegated code to precompile, treat precompile always as warm account when resolving code Signed-off-by: Daniel Lehrner * make accessListWarmAddresses generic again Signed-off-by: Daniel Lehrner * warm delegatee account if transaction destination has delegated code Signed-off-by: Daniel Lehrner * * 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 * generalised requests flat encoding and engine api changes Signed-off-by: Jason Frame * javadoc Signed-off-by: Jason Frame * get tests passing Signed-off-by: Jason Frame * get tests passing Signed-off-by: Jason Frame * clean code Signed-off-by: Jason Frame * change requests to single requestData for each requestType Signed-off-by: Jason Frame * fix PoWBlockCreatorTest after requests data type change Signed-off-by: Jason Frame * don't return request type in getPayload result Signed-off-by: Jason Frame don't return request type in getPayload result Signed-off-by: Jason Frame * include requests in t8n response Signed-off-by: Jason Frame * update contract addresses for consolidation requests and withdrawal requests Signed-off-by: Jason Frame * fix requestHash calculation Signed-off-by: Jason Frame * Ensure that execution requests always return a response Signed-off-by: Jason Frame * added and fixed bound checks, fixed some compilation errors after the rebase Signed-off-by: Daniel Lehrner * revert changes to evm tool spec tests Signed-off-by: Jason Frame * clean up Signed-off-by: Jason Frame * replace AbstractSystemCallRequestProcessor to concrete class and remove specific processors Signed-off-by: Jason Frame * spotless Signed-off-by: Jason Frame * update evmtool tests for 7685 changes Signed-off-by: Jason Frame * use empty requests hash prague fork at genesis Signed-off-by: Jason Frame * review suggestions Signed-off-by: Jason Frame * temporarily comment out osakaTime from Prague Signed-off-by: Daniel Lehrner * engine API validation Signed-off-by: Jason Frame * update plugin API hash Signed-off-by: Jason Frame * fix GenesisStateTest Signed-off-by: Jason Frame * comment out unused evmWorldUpdater.parentUpdater() check Signed-off-by: Daniel Lehrner * added CodeDelegationProcessorTest Signed-off-by: Daniel Lehrner * code clean up Signed-off-by: Daniel Lehrner * spotless Signed-off-by: Daniel Lehrner --------- Signed-off-by: Daniel Lehrner Signed-off-by: Jason Frame Co-authored-by: Jason Frame --- .../besu/crypto/AbstractSECP256.java | 2 +- .../besu/crypto/CodeDelegationSignature.java | 9 +- .../besu/crypto/SignatureAlgorithm.java | 2 +- .../crypto/CodeDelegationSignatureTest.java | 27 +- .../besu/datatypes/CodeDelegation.java | 2 +- .../execution/TracedJsonRpcProcessor.java | 1 + .../internal/response/RpcErrorType.java | 1 + .../besu/ethereum/core/CodeDelegation.java | 4 +- .../besu/ethereum/core/Transaction.java | 5 +- .../CodeDelegationTransactionDecoder.java | 2 +- ... => CodeDelegationTransactionEncoder.java} | 6 +- .../core/encoding/TransactionEncoder.java | 2 +- .../mainnet/CodeDelegationProcessor.java | 27 +- .../mainnet/MainnetProtocolSpecs.java | 10 +- .../mainnet/MainnetTransactionProcessor.java | 12 +- .../mainnet/MainnetTransactionValidator.java | 22 +- ...CodeDelegationTransactionEncoderTest.java} | 8 +- .../mainnet/CodeDelegationProcessorTest.java | 236 ++++++++++++++++++ .../MainnetTransactionProcessorTest.java | 2 +- .../hyperledger/besu/evmtool/T8nExecutor.java | 12 +- .../evm/account/BaseDelegatedCodeAccount.java | 10 +- .../evm/account/DelegatedCodeAccount.java | 7 +- .../account/MutableDelegatedCodeAccount.java | 7 +- .../besu/evm/gascalculator/GasCalculator.java | 10 - .../gascalculator/PragueGasCalculator.java | 5 - .../DelegatedCodeGasCostHelper.java | 14 +- .../evm/worldstate/DelegatedCodeService.java | 31 ++- .../besu/evm/worldstate/EVMWorldUpdater.java | 11 +- 28 files changed, 391 insertions(+), 96 deletions(-) rename ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/{CodeDelegationEncoder.java => CodeDelegationTransactionEncoder.java} (95%) rename ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/{CodeDelegationEncoderTest.java => CodeDelegationTransactionEncoderTest.java} (93%) create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessorTest.java diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java index bd450b206e9..4e228441cc4 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/AbstractSECP256.java @@ -214,7 +214,7 @@ public SECPSignature createSignature(final BigInteger r, final BigInteger s, fin @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); } diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java index 06ec72bf0a9..e68f30727ca 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/CodeDelegationSignature.java @@ -42,7 +42,7 @@ public CodeDelegationSignature(final BigInteger r, final BigInteger s, final byt * @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 static CodeDelegationSignature create( "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); } } diff --git a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java index 4bf8d89c825..a1a79d057a5 100644 --- a/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java +++ b/crypto/algorithms/src/main/java/org/hyperledger/besu/crypto/SignatureAlgorithm.java @@ -224,7 +224,7 @@ Optional recoverPublicKeyFromSignature( * @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. diff --git a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java index 332aa14893f..1c0cc95ffd9 100644 --- a/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java +++ b/crypto/algorithms/src/test/java/org/hyperledger/besu/crypto/CodeDelegationSignatureTest.java @@ -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 @@ void testNullRValue() { @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 @@ void testNullSValue() { 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 @@ void testRValueExceedsTwoPow256() { 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()); } } diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/CodeDelegation.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/CodeDelegation.java index 7b9e3d7d447..aab8203e296 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/CodeDelegation.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/CodeDelegation.java @@ -25,7 +25,7 @@ */ 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. diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java index 2a38afd630c..4ac790617fd 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/TracedJsonRpcProcessor.java @@ -107,6 +107,7 @@ public JsonRpcResponse process( 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: diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java index a2b7d175918..fa67fce15bc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/RpcErrorType.java @@ -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"), diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/CodeDelegation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/CodeDelegation.java index 68fa958a8d5..ef38a982224 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/CodeDelegation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/CodeDelegation.java @@ -20,7 +20,7 @@ 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 BigInteger s() { private Optional
computeAuthority() { BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); - CodeDelegationEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput); + CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput); final Hash hash = Hash.hash(Bytes.concatenate(MAGIC, rlpOutput.encoded())); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 0da8f8f28a2..1926fa8a7d3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -38,7 +38,7 @@ 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 @@ private static Bytes codeDelegationPreimage( chainId, accessList, rlpOutput); - CodeDelegationEncoder.encodeCodeDelegationInner(authorizationList, rlpOutput); + CodeDelegationTransactionEncoder.encodeCodeDelegationInner( + authorizationList, rlpOutput); rlpOutput.endList(); }); return Bytes.concatenate(Bytes.of(TransactionType.DELEGATE_CODE.getSerializedType()), encoded); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java index 6448940d8d5..88d502f7a18 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionDecoder.java @@ -81,7 +81,7 @@ public static CodeDelegation decodeInnerPayload(final RLPInput input) { 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(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoder.java similarity index 95% rename from ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoder.java rename to ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoder.java index 4cedf93adc2..c34de129251 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoder.java @@ -25,9 +25,9 @@ import org.apache.tuweni.bytes.Bytes; -public class CodeDelegationEncoder { +public class CodeDelegationTransactionEncoder { - private CodeDelegationEncoder() { + private CodeDelegationTransactionEncoder() { // private constructor } @@ -49,7 +49,7 @@ public static void encodeSingleCodeDelegation( 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(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java index 26bad56c6da..a97f24cbd04 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java @@ -41,7 +41,7 @@ interface Encoder { TransactionType.BLOB, BlobTransactionEncoder::encode, TransactionType.DELEGATE_CODE, - CodeDelegationEncoder::encode); + CodeDelegationTransactionEncoder::encode); private static final ImmutableMap POOLED_TRANSACTION_ENCODERS = ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessor.java index 58f66b6afb4..977b5031936 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessor.java @@ -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 maybeChainId; + private final BigInteger halfCurveOrder; - public CodeDelegationProcessor(final Optional maybeChainId) { + public CodeDelegationProcessor( + final Optional maybeChainId, final BigInteger halfCurveOrder) { this.maybeChainId = maybeChainId; + this.halfCurveOrder = halfCurveOrder; } /** @@ -89,6 +94,22 @@ private void processAuthorization( 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
authorizer = codeDelegation.authorizer(); if (authorizer.isEmpty()) { LOG.trace("Invalid signature for code delegation"); @@ -128,7 +149,9 @@ private void processAuthorization( result.incremenentAlreadyExistingDelegators(); } - evmWorldUpdater.authorizedCodeService().addDelegatedCode(authority, codeDelegation.address()); + evmWorldUpdater + .authorizedCodeService() + .processDelegatedCodeAuthorization(authority, codeDelegation.address()); authority.incrementNonce(); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 87396bff3e9..26c9e69c8f5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -18,6 +18,8 @@ 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.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 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 @@ static ProtocolSpecBuilder cancunDefinition( 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) -> diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index c662c188644..3425708247a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -286,7 +286,7 @@ public TransactionProcessingResult processTransaction( 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 TransactionProcessingResult processTransaction( codeDelegationRefund = gasCalculator.calculateDelegateCodeGasRefund( (codeDelegationResult.alreadyExistingDelegators())); + + evmWorldUpdater.commit(); } final List accessListEntries = transaction.getAccessList().orElse(List.of()); @@ -415,7 +417,6 @@ public TransactionProcessingResult processTransaction( .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) .contextVariables(contextVariablesBuilder.build()) - .accessListWarmAddresses(warmAddressList) .accessListWarmStorage(storageList); if (transaction.getVersionedHashes().isPresent()) { @@ -439,11 +440,17 @@ public TransactionProcessingResult processTransaction( .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 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 TransactionProcessingResult processTransaction( maybeContract .map(c -> messageCallProcessor.getCodeFromEVM(c.getCodeHash(), c.getCode())) .orElse(CodeV0.EMPTY_CODE)) + .accessListWarmAddresses(warmAddressList) .build(); } Deque messageFrameStack = initialFrame.getMessageFrameStack(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index 0e86b6b8783..2b9fea0f3d0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -52,6 +52,8 @@ */ 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 @@ private static ValidationResult validateCodeDelegation "transaction code delegation transactions must have a to address"); } - final BigInteger halfCurveOrder = SignatureAlgorithmFactory.getInstance().getHalfCurveOrder(); final Optional> 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()); } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoderTest.java similarity index 93% rename from ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoderTest.java rename to ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoderTest.java index 34b7c6d4491..b09fde48dd1 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationEncoderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/CodeDelegationTransactionEncoderTest.java @@ -30,7 +30,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class CodeDelegationEncoderTest { +class CodeDelegationTransactionEncoderTest { private static final Supplier SIGNATURE_ALGORITHM = Suppliers.memoize(SignatureAlgorithmFactory::getInstance); @@ -59,7 +59,7 @@ void shouldEncodeSingleCodeDelegationWithNonceAndChainId() { "3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16), (byte) 0)); - CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); + CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output); assertThat(output.encoded()) .isEqualTo( @@ -85,7 +85,7 @@ void shouldEncodeSingleCodeDelegationWithNonceZero() { "25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16), (byte) 1)); - CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); + CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output); assertThat(output.encoded()) .isEqualTo( @@ -111,7 +111,7 @@ void shouldEncodeSingleCodeDelegationWithChainIdZero() { "3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16), (byte) 1)); - CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); + CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output); assertThat(output.encoded()) .isEqualTo( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessorTest.java new file mode 100644 index 00000000000..c4b45f9e08c --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CodeDelegationProcessorTest.java @@ -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); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index c40da7210e2..3403dc0b394 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -89,7 +89,7 @@ MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbas MAX_STACK_SIZE, FeeMarket.legacy(), CoinbaseFeePriceCalculator.frontier(), - new CodeDelegationProcessor(Optional.of(BigInteger.ONE))); + new CodeDelegationProcessor(Optional.of(BigInteger.ONE), BigInteger.TEN)); } @Test diff --git a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java index ba55fdf29ea..8c64a031b3f 100644 --- a/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java +++ b/ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java @@ -231,10 +231,14 @@ protected static List extractTransactions( 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 @@ protected static List extractTransactions( .toUnsignedBigInteger(); final SECPSignature authorizationSignature = - new SECPSignature(authorizationR, authorizationS, authorizationV); + new SECPSignature(authorizationR, authorizationS, authorizationV.byteValue()); authorizations.add( new org.hyperledger.besu.ethereum.core.CodeDelegation( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/BaseDelegatedCodeAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/BaseDelegatedCodeAccount.java index 0e5219d8355..4ee28145def 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/account/BaseDelegatedCodeAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/BaseDelegatedCodeAccount.java @@ -17,6 +17,7 @@ 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 @@ 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 @@ private Optional getDelegatedAccount() { } private Bytes resolveDelegatedCode() { + if (gasCalculator.isPrecompile(delegatedCodeAddress)) { + return Bytes.EMPTY; + } return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(Bytes.EMPTY); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/DelegatedCodeAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/DelegatedCodeAccount.java index 1eba364c194..0ac969abade 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/account/DelegatedCodeAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/DelegatedCodeAccount.java @@ -17,6 +17,7 @@ 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; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/account/MutableDelegatedCodeAccount.java b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableDelegatedCodeAccount.java index 0e1e1145dd6..20894b4f286 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/account/MutableDelegatedCodeAccount.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/account/MutableDelegatedCodeAccount.java @@ -17,6 +17,7 @@ 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; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index fd453deac51..4c6770e70d2 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -666,14 +666,4 @@ default long delegateCodeGasCost(final int delegateCodeListLength) { 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; - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index 33fe98b9968..666292e9e10 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -52,9 +52,4 @@ public long delegateCodeGasCost(final int delegateCodeListLength) { public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) { return existingAccountGasRefund * alreadyExistingAccounts; } - - @Override - public long delegatedCodeResolutionGasCost(final boolean isWarm) { - return isWarm ? getWarmStorageReadCost() : getColdAccountAccessCost(); - } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeGasCostHelper.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeGasCostHelper.java index fc677703147..3cdd2dc205d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeGasCostHelper.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeGasCostHelper.java @@ -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 static Result deductDelegatedCodeGasCost( 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 static Result deductDelegatedCodeGasCost( 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(); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java index 885324b10b6..770d0b79ddc 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java @@ -19,6 +19,7 @@ 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 Account processAccount(final WorldUpdater worldUpdater, final Account acc } return new DelegatedCodeAccount( - worldUpdater, account, resolveDelegatedAddress(account.getCode())); + worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator); } /** @@ -82,7 +99,7 @@ public MutableAccount processMutableAccount( } return new MutableDelegatedCodeAccount( - worldUpdater, account, resolveDelegatedAddress(account.getCode())); + worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator); } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/EVMWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/EVMWorldUpdater.java index 928118acfed..f6e45f4555f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/EVMWorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/EVMWorldUpdater.java @@ -19,6 +19,7 @@ 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 void commit() { @Override public Optional parentUpdater() { - return rootWorldUpdater.parentUpdater(); + return rootWorldUpdater.parentUpdater().isPresent() + ? Optional.of( + new EVMWorldUpdater(rootWorldUpdater.parentUpdater().get(), delegatedCodeService)) + : Optional.empty(); } @Override