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 @Override
public CodeDelegationSignature createCodeDelegationSignature( 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); return CodeDelegationSignature.create(r, s, yParity);
} }

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

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

@ -29,19 +29,19 @@ class CodeDelegationSignatureTest {
void testValidInputs() { void testValidInputs() {
BigInteger r = BigInteger.ONE; BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN; BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ONE; byte yParity = (byte) 1;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity); CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR()); assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS()); assertThat(s).isEqualTo(result.getS());
assertThat(yParity.byteValue()).isEqualTo(result.getRecId()); assertThat(yParity).isEqualTo(result.getRecId());
} }
@Test @Test
void testNullRValue() { void testNullRValue() {
BigInteger s = BigInteger.TEN; BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO; byte yParity = (byte) 0;
assertThatExceptionOfType(NullPointerException.class) assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(null, s, yParity)); .isThrownBy(() -> CodeDelegationSignature.create(null, s, yParity));
@ -50,7 +50,7 @@ class CodeDelegationSignatureTest {
@Test @Test
void testNullSValue() { void testNullSValue() {
BigInteger r = BigInteger.ONE; BigInteger r = BigInteger.ONE;
BigInteger yParity = BigInteger.ZERO; byte yParity = (byte) 0;
assertThatExceptionOfType(NullPointerException.class) assertThatExceptionOfType(NullPointerException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, null, yParity)); .isThrownBy(() -> CodeDelegationSignature.create(r, null, yParity));
@ -60,7 +60,7 @@ class CodeDelegationSignatureTest {
void testRValueExceedsTwoPow256() { void testRValueExceedsTwoPow256() {
BigInteger r = TWO_POW_256; BigInteger r = TWO_POW_256;
BigInteger s = BigInteger.TEN; BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO; byte yParity = (byte) 0;
assertThatExceptionOfType(IllegalArgumentException.class) assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity)) .isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
@ -71,34 +71,23 @@ class CodeDelegationSignatureTest {
void testSValueExceedsTwoPow256() { void testSValueExceedsTwoPow256() {
BigInteger r = BigInteger.ONE; BigInteger r = BigInteger.ONE;
BigInteger s = TWO_POW_256; BigInteger s = TWO_POW_256;
BigInteger yParity = BigInteger.ZERO; byte yParity = (byte) 0;
assertThatExceptionOfType(IllegalArgumentException.class) assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity)) .isThrownBy(() -> CodeDelegationSignature.create(r, s, yParity))
.withMessageContainingAll("Invalid 's' value, should be < 2^256"); .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 @Test
void testValidYParityZero() { void testValidYParityZero() {
BigInteger r = BigInteger.ONE; BigInteger r = BigInteger.ONE;
BigInteger s = BigInteger.TEN; BigInteger s = BigInteger.TEN;
BigInteger yParity = BigInteger.ZERO; byte yParity = (byte) 0;
CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity); CodeDelegationSignature result = CodeDelegationSignature.create(r, s, yParity);
assertThat(r).isEqualTo(result.getR()); assertThat(r).isEqualTo(result.getR());
assertThat(s).isEqualTo(result.getS()); 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 { public interface CodeDelegation {
/** The cost of delegating code on an existing account. */ /** 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. * Return the chain id.

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

@ -95,6 +95,7 @@ public enum RpcErrorType implements RpcMethodError {
INVALID_REMOTE_CAPABILITIES_PARAMS( INVALID_REMOTE_CAPABILITIES_PARAMS(
INVALID_PARAMS_ERROR_CODE, "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_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_SEALER_ID_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid sealer ID params"),
INVALID_STORAGE_KEYS_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid storage keys params"), INVALID_STORAGE_KEYS_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid storage keys params"),
INVALID_SUBSCRIPTION_PARAMS(INVALID_PARAMS_ERROR_CODE, "Invalid subscription 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.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash; 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 org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.math.BigInteger; import java.math.BigInteger;
@ -140,7 +140,7 @@ public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelega
private Optional<Address> computeAuthority() { private Optional<Address> computeAuthority() {
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
CodeDelegationEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput); CodeDelegationTransactionEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput);
final Hash hash = Hash.hash(Bytes.concatenate(MAGIC, rlpOutput.encoded())); 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.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder;
import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder; 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.EncodingContext;
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder; import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
@ -937,7 +937,8 @@ public class Transaction
chainId, chainId,
accessList, accessList,
rlpOutput); rlpOutput);
CodeDelegationEncoder.encodeCodeDelegationInner(authorizationList, rlpOutput); CodeDelegationTransactionEncoder.encodeCodeDelegationInner(
authorizationList, rlpOutput);
rlpOutput.endList(); rlpOutput.endList();
}); });
return Bytes.concatenate(Bytes.of(TransactionType.DELEGATE_CODE.getSerializedType()), encoded); 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 Address address = Address.wrap(input.readBytes());
final long nonce = input.readLongScalar(); 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 r = input.readUInt256Scalar().toUnsignedBigInteger();
final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger(); final BigInteger s = input.readUInt256Scalar().toUnsignedBigInteger();

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

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

@ -14,6 +14,8 @@
*/ */
package org.hyperledger.besu.ethereum.mainnet; 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.datatypes.Address;
import org.hyperledger.besu.ethereum.core.CodeDelegation; import org.hyperledger.besu.ethereum.core.CodeDelegation;
import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Transaction;
@ -30,9 +32,12 @@ public class CodeDelegationProcessor {
private static final Logger LOG = LoggerFactory.getLogger(CodeDelegationProcessor.class); private static final Logger LOG = LoggerFactory.getLogger(CodeDelegationProcessor.class);
private final Optional<BigInteger> maybeChainId; 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.maybeChainId = maybeChainId;
this.halfCurveOrder = halfCurveOrder;
} }
/** /**
@ -89,6 +94,22 @@ public class CodeDelegationProcessor {
return; 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(); final Optional<Address> authorizer = codeDelegation.authorizer();
if (authorizer.isEmpty()) { if (authorizer.isEmpty()) {
LOG.trace("Invalid signature for code delegation"); LOG.trace("Invalid signature for code delegation");
@ -128,7 +149,9 @@ public class CodeDelegationProcessor {
result.incremenentAlreadyExistingDelegators(); result.incremenentAlreadyExistingDelegators();
} }
evmWorldUpdater.authorizedCodeService().addDelegatedCode(authority, codeDelegation.address()); evmWorldUpdater
.authorizedCodeService()
.processDelegatedCodeAuthorization(authority, codeDelegation.address());
authority.incrementNonce(); 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.GenesisConfigOptions;
import org.hyperledger.besu.config.PowAlgorithm; 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.Address;
import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
@ -80,6 +82,8 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonArray;
@ -89,6 +93,9 @@ public abstract class MainnetProtocolSpecs {
private static final Address RIPEMD160_PRECOMPILE = private static final Address RIPEMD160_PRECOMPILE =
Address.fromHexString("0x0000000000000000000000000000000000000003"); Address.fromHexString("0x0000000000000000000000000000000000000003");
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
// A consensus bug at Ethereum mainnet transaction 0xcf416c53 // A consensus bug at Ethereum mainnet transaction 0xcf416c53
// deleted an empty account even when the message execution scope // deleted an empty account even when the message execution scope
// failed, but the transaction itself succeeded. // failed, but the transaction itself succeeded.
@ -714,7 +721,8 @@ public abstract class MainnetProtocolSpecs {
evmConfiguration.evmStackSize(), evmConfiguration.evmStackSize(),
feeMarket, feeMarket,
CoinbaseFeePriceCalculator.eip1559(), CoinbaseFeePriceCalculator.eip1559(),
new CodeDelegationProcessor(chainId))) new CodeDelegationProcessor(
chainId, SIGNATURE_ALGORITHM.get().getHalfCurveOrder())))
// change to check for max blob gas per block for EIP-4844 // change to check for max blob gas per block for EIP-4844
.transactionValidatorFactoryBuilder( .transactionValidatorFactoryBuilder(
(evm, gasLimitCalculator, feeMarket) -> (evm, gasLimitCalculator, feeMarket) ->

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

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

@ -30,7 +30,7 @@ import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
class CodeDelegationEncoderTest { class CodeDelegationTransactionEncoderTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM = private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
@ -59,7 +59,7 @@ class CodeDelegationEncoderTest {
"3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16), "3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16),
(byte) 0)); (byte) 0));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded()) assertThat(output.encoded())
.isEqualTo( .isEqualTo(
@ -85,7 +85,7 @@ class CodeDelegationEncoderTest {
"25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16), "25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16),
(byte) 1)); (byte) 1));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded()) assertThat(output.encoded())
.isEqualTo( .isEqualTo(
@ -111,7 +111,7 @@ class CodeDelegationEncoderTest {
"3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16), "3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16),
(byte) 1)); (byte) 1));
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output); CodeDelegationTransactionEncoder.encodeSingleCodeDelegation(authorization, output);
assertThat(output.encoded()) assertThat(output.encoded())
.isEqualTo( .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, MAX_STACK_SIZE,
FeeMarket.legacy(), FeeMarket.legacy(),
CoinbaseFeePriceCalculator.frontier(), CoinbaseFeePriceCalculator.frontier(),
new CodeDelegationProcessor(Optional.of(BigInteger.ONE))); new CodeDelegationProcessor(Optional.of(BigInteger.ONE), BigInteger.TEN));
} }
@Test @Test

@ -231,10 +231,14 @@ public class T8nExecutor {
final long authorizationNonce = final long authorizationNonce =
Bytes.fromHexStringLenient(entryAsJson.get("nonce").textValue()).toLong(); Bytes.fromHexStringLenient(entryAsJson.get("nonce").textValue()).toLong();
final byte authorizationV = final BigInteger authorizationV =
Bytes.fromHexStringLenient(entryAsJson.get("v").textValue()) Bytes.fromHexStringLenient(entryAsJson.get("v").textValue())
.toUnsignedBigInteger() .toUnsignedBigInteger();
.byteValueExact(); if (authorizationV.compareTo(BigInteger.valueOf(256)) >= 0) {
throw new IllegalArgumentException(
"Invalid authorizationV value. Must be less than 256");
}
final BigInteger authorizationR = final BigInteger authorizationR =
Bytes.fromHexStringLenient(entryAsJson.get("r").textValue()) Bytes.fromHexStringLenient(entryAsJson.get("r").textValue())
.toUnsignedBigInteger(); .toUnsignedBigInteger();
@ -243,7 +247,7 @@ public class T8nExecutor {
.toUnsignedBigInteger(); .toUnsignedBigInteger();
final SECPSignature authorizationSignature = final SECPSignature authorizationSignature =
new SECPSignature(authorizationR, authorizationS, authorizationV); new SECPSignature(authorizationR, authorizationS, authorizationV.byteValue());
authorizations.add( authorizations.add(
new org.hyperledger.besu.ethereum.core.CodeDelegation( 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.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Optional; import java.util.Optional;
@ -25,13 +26,17 @@ import org.apache.tuweni.bytes.Bytes;
class BaseDelegatedCodeAccount { class BaseDelegatedCodeAccount {
private final WorldUpdater worldUpdater; private final WorldUpdater worldUpdater;
private final GasCalculator gasCalculator;
/** The address of the account that has delegated code to be loaded into it. */ /** The address of the account that has delegated code to be loaded into it. */
protected final Address delegatedCodeAddress; protected final Address delegatedCodeAddress;
protected BaseDelegatedCodeAccount( protected BaseDelegatedCodeAccount(
final WorldUpdater worldUpdater, final Address delegatedCodeAddress) { final WorldUpdater worldUpdater,
final Address delegatedCodeAddress,
final GasCalculator gasCalculator) {
this.worldUpdater = worldUpdater; this.worldUpdater = worldUpdater;
this.gasCalculator = gasCalculator;
this.delegatedCodeAddress = delegatedCodeAddress; this.delegatedCodeAddress = delegatedCodeAddress;
} }
@ -86,6 +91,9 @@ class BaseDelegatedCodeAccount {
} }
private Bytes resolveDelegatedCode() { private Bytes resolveDelegatedCode() {
if (gasCalculator.isPrecompile(delegatedCodeAddress)) {
return Bytes.EMPTY;
}
return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(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.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.NavigableMap; import java.util.NavigableMap;
@ -37,12 +38,14 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
* @param worldUpdater the world updater. * @param worldUpdater the world updater.
* @param wrappedAccount the account that has delegated code to be loaded into it. * @param wrappedAccount the account that has delegated code to be loaded into it.
* @param codeDelegationAddress the address of the delegated code. * @param codeDelegationAddress the address of the delegated code.
* @param gasCalculator the gas calculator to check for precompiles.
*/ */
public DelegatedCodeAccount( public DelegatedCodeAccount(
final WorldUpdater worldUpdater, final WorldUpdater worldUpdater,
final Account wrappedAccount, final Account wrappedAccount,
final Address codeDelegationAddress) { final Address codeDelegationAddress,
super(worldUpdater, codeDelegationAddress); final GasCalculator gasCalculator) {
super(worldUpdater, codeDelegationAddress, gasCalculator);
this.wrappedAccount = wrappedAccount; this.wrappedAccount = wrappedAccount;
} }

@ -17,6 +17,7 @@ package org.hyperledger.besu.evm.account;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Map; import java.util.Map;
@ -39,12 +40,14 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
* @param worldUpdater the world updater. * @param worldUpdater the world updater.
* @param wrappedAccount the account that has delegated code to be loaded into it. * @param wrappedAccount the account that has delegated code to be loaded into it.
* @param codeDelegationAddress the address of the delegated code. * @param codeDelegationAddress the address of the delegated code.
* @param gasCalculator the gas calculator to check for precompiles.
*/ */
public MutableDelegatedCodeAccount( public MutableDelegatedCodeAccount(
final WorldUpdater worldUpdater, final WorldUpdater worldUpdater,
final MutableAccount wrappedAccount, final MutableAccount wrappedAccount,
final Address codeDelegationAddress) { final Address codeDelegationAddress,
super(worldUpdater, codeDelegationAddress); final GasCalculator gasCalculator) {
super(worldUpdater, codeDelegationAddress, gasCalculator);
this.wrappedAccount = wrappedAccount; this.wrappedAccount = wrappedAccount;
} }

@ -666,14 +666,4 @@ public interface GasCalculator {
default long calculateDelegateCodeGasRefund(final long alreadyExistingAccountSize) { default long calculateDelegateCodeGasRefund(final long alreadyExistingAccountSize) {
return 0L; 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) { public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) {
return existingAccountGasRefund * 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; package org.hyperledger.besu.evm.worldstate;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; 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"); throw new RuntimeException("A delegated code account must have a delegated code address");
} }
final boolean delegatedCodeIsWarm = frame.warmUpAddress(account.delegatedCodeAddress().get());
final long delegatedCodeResolutionGas = final long delegatedCodeResolutionGas =
gasCalculator.delegatedCodeResolutionGasCost(delegatedCodeIsWarm); calculateDelegatedCodeResolutionGas(
frame, gasCalculator, account.delegatedCodeAddress().get());
if (frame.getRemainingGas() < delegatedCodeResolutionGas) { if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS); return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS);
@ -77,4 +78,13 @@ public class DelegatedCodeGasCostHelper {
frame.decrementRemainingGas(delegatedCodeResolutionGas); frame.decrementRemainingGas(delegatedCodeResolutionGas);
return new Result(delegatedCodeResolutionGas, Status.SUCCESS); 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.DelegatedCodeAccount;
import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.account.MutableDelegatedCodeAccount; import org.hyperledger.besu.evm.account.MutableDelegatedCodeAccount;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.apache.tuweni.bytes.Bytes; 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 Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
private static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE; private static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
/** Creates a new DelegatedCodeService. */ private final GasCalculator gasCalculator;
public DelegatedCodeService() {}
/** /**
* 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 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)); account.setCode(Bytes.concatenate(DELEGATED_CODE_PREFIX, delegatedCodeAddress));
} }
@ -64,7 +81,7 @@ public class DelegatedCodeService {
} }
return new DelegatedCodeAccount( return new DelegatedCodeAccount(
worldUpdater, account, resolveDelegatedAddress(account.getCode())); worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator);
} }
/** /**
@ -82,7 +99,7 @@ public class DelegatedCodeService {
} }
return new MutableDelegatedCodeAccount( 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.Account;
import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
@ -35,9 +36,10 @@ public class EVMWorldUpdater implements WorldUpdater {
* Instantiates a new EVM world updater. * Instantiates a new EVM world updater.
* *
* @param rootWorldUpdater the root world updater * @param rootWorldUpdater the root world updater
* @param gasCalculator the gas calculator to check for precompiles.
*/ */
public EVMWorldUpdater(final WorldUpdater rootWorldUpdater) { public EVMWorldUpdater(final WorldUpdater rootWorldUpdater, final GasCalculator gasCalculator) {
this(rootWorldUpdater, new DelegatedCodeService()); this(rootWorldUpdater, new DelegatedCodeService(gasCalculator));
} }
private EVMWorldUpdater( private EVMWorldUpdater(
@ -110,7 +112,10 @@ public class EVMWorldUpdater implements WorldUpdater {
@Override @Override
public Optional<WorldUpdater> parentUpdater() { public Optional<WorldUpdater> parentUpdater() {
return rootWorldUpdater.parentUpdater(); return rootWorldUpdater.parentUpdater().isPresent()
? Optional.of(
new EVMWorldUpdater(rootWorldUpdater.parentUpdater().get(), delegatedCodeService))
: Optional.empty();
} }
@Override @Override

Loading…
Cancel
Save