mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 21:38:15 -05:00
7702 for devenet-3 (#7444)
* wrapped WorldUpdater into `EVMWorldupdater` to remove the authority code injection from the implementation of the actual world updaters Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * add CHANGELOG entry Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * first draft for 7702 v2 Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * change return value of DelegatedCodeGasCostHelper Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fix upfront gas cost calculation, fix setting code multiple times in MutableDelegatedCodeAccount Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fix gas refund for delegated code when account already exists, added gas cost deduction for code delegation resolution to ExtCodeSizeOperation Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * allow accounts with delegated code to send transactions Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * add refund for already existing account after nonce check Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * resolve delegated code only the first time to avoid delegation loops Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * handle invalid authorization signatures properly Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * refactored CodeDelegationProcessor to compute authorizer of a code delegation after the chain id check Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fix canSetDelegatedCode method by checking code how it is in the trie and not the resolved code Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * optimize code hash calculation for empty code, fix check for empty code delegation list Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * check the all code delegation signatures hava a valid s value Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * rename encoder & decoder, handle invalid signature values in T8nExecutor Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * create the signatures for code delegation authorizations in T8nExecutor without checking if they are valid to test them later during the tx execution Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * check that recid is either 0 or 1 Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fixed acceptance tests, renamed the the remaining instances of set code to code delegation Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fix delegate encoder & encoder unit tests Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * spotless Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * changed empty check for delegated accounts, fixed test Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * fix hasDelegatedCode method when code is null Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * run acceptance tests without deamon Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * disable CodeDelegationTransactionAcceptanceTest to check if it is causing the stuck ci pipeline Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * check if shouldTransferAllEthOfAuthorizerToSponsor is causing pipeline to stall Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * check if shouldCheckNonceAfterNonceIncreaseOfSender is causing pipeline to stall Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net> * check if closing the cluster after every test is causing pipeline to stall 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> Co-authored-by: Danno Ferrin <danno@numisight.com>
This commit is contained in:
@@ -18,9 +18,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
@@ -39,7 +39,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.web3j.protocol.core.methods.response.TransactionReceipt;
|
||||
|
||||
public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
public class CodeDelegationTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
private static final String GENESIS_FILE = "/dev/dev_prague.json";
|
||||
private static final SECP256K1 secp256k1 = new SECP256K1();
|
||||
|
||||
@@ -74,7 +74,6 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
besuNode.close();
|
||||
cluster.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,17 +87,18 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
public void shouldTransferAllEthOfAuthorizerToSponsor() throws IOException {
|
||||
|
||||
// 7702 transaction
|
||||
final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization =
|
||||
SetCodeAuthorization.builder()
|
||||
final CodeDelegation authorization =
|
||||
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
|
||||
.chainId(BigInteger.valueOf(20211))
|
||||
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
|
||||
.nonce(0)
|
||||
.signAndBuild(
|
||||
secp256k1.createKeyPair(
|
||||
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
|
||||
|
||||
final Transaction tx =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.SET_CODE)
|
||||
.type(TransactionType.DELEGATE_CODE)
|
||||
.chainId(BigInteger.valueOf(20211))
|
||||
.nonce(0)
|
||||
.maxPriorityFeePerGas(Wei.of(1000000000))
|
||||
@@ -108,7 +108,7 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
.value(Wei.ZERO)
|
||||
.payload(Bytes32.leftPad(Bytes.fromHexString(transactionSponsor.getAddress())))
|
||||
.accessList(List.of())
|
||||
.setCodeTransactionPayloads(List.of(authorization))
|
||||
.codeDelegations(List.of(authorization))
|
||||
.signAndBuild(
|
||||
secp256k1.createKeyPair(
|
||||
secp256k1.createPrivateKey(
|
||||
@@ -134,22 +134,21 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
|
||||
/**
|
||||
* The authorizer creates an authorization for a contract that sends all its ETH to any given
|
||||
* address. But the nonce is 1 and the authorization list is processed before the nonce increase
|
||||
* of the sender. Therefore, the authorization should be invalid and will be ignored. No balance
|
||||
* change, except for a decrease for paying the transaction cost should occur.
|
||||
* address. The nonce is 1 and the authorization list is processed after the nonce increase of the
|
||||
* sender. Therefore, the authorization should be valid. The authorizer balance should be 0 and
|
||||
* the transaction sponsor's * balance should be 180000 ETH minus the transaction costs.
|
||||
*/
|
||||
@Test
|
||||
public void shouldCheckNonceBeforeNonceIncreaseOfSender() throws IOException {
|
||||
|
||||
public void shouldCheckNonceAfterNonceIncreaseOfSender() throws IOException {
|
||||
final long GAS_LIMIT = 1000000L;
|
||||
cluster.verify(authorizer.balanceEquals(Amount.ether(90000)));
|
||||
|
||||
final org.hyperledger.besu.datatypes.SetCodeAuthorization authorization =
|
||||
SetCodeAuthorization.builder()
|
||||
final CodeDelegation authorization =
|
||||
org.hyperledger.besu.ethereum.core.CodeDelegation.builder()
|
||||
.chainId(BigInteger.valueOf(20211))
|
||||
.nonces(
|
||||
Optional.of(
|
||||
1L)) // nonce is 1, but because it is validated before the nonce increase, it
|
||||
// should be 0
|
||||
.nonce(
|
||||
1L) // nonce is 1, but because it is validated before the nonce increase, it should
|
||||
// be 0
|
||||
.address(SEND_ALL_ETH_CONTRACT_ADDRESS)
|
||||
.signAndBuild(
|
||||
secp256k1.createKeyPair(
|
||||
@@ -157,17 +156,17 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
|
||||
final Transaction tx =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.SET_CODE)
|
||||
.type(TransactionType.DELEGATE_CODE)
|
||||
.chainId(BigInteger.valueOf(20211))
|
||||
.nonce(0)
|
||||
.maxPriorityFeePerGas(Wei.of(1000000000))
|
||||
.maxFeePerGas(Wei.fromHexString("0x02540BE400"))
|
||||
.gasLimit(1000000)
|
||||
.gasLimit(GAS_LIMIT)
|
||||
.to(Address.fromHexStringStrict(authorizer.getAddress()))
|
||||
.value(Wei.ZERO)
|
||||
.payload(Bytes32.leftPad(Bytes.fromHexString(otherAccount.getAddress())))
|
||||
.accessList(List.of())
|
||||
.setCodeTransactionPayloads(List.of(authorization))
|
||||
.codeDelegations(List.of(authorization))
|
||||
.signAndBuild(
|
||||
secp256k1.createKeyPair(
|
||||
secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger())));
|
||||
@@ -180,14 +179,25 @@ public class SetCodeTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
|
||||
assertThat(maybeTransactionReceipt).isPresent();
|
||||
|
||||
// verify that the balance of the other account has not changed
|
||||
cluster.verify(otherAccount.balanceEquals(0));
|
||||
|
||||
final String gasPriceWithout0x =
|
||||
maybeTransactionReceipt.get().getEffectiveGasPrice().substring(2);
|
||||
final BigInteger txCost =
|
||||
maybeTransactionReceipt.get().getGasUsed().multiply(new BigInteger(gasPriceWithout0x, 16));
|
||||
BigInteger expectedSenderBalance = new BigInteger("90000000000000000000000").subtract(txCost);
|
||||
cluster.verify(authorizer.balanceEquals(Amount.wei(expectedSenderBalance)));
|
||||
final BigInteger gasPrice = new BigInteger(gasPriceWithout0x, 16);
|
||||
final BigInteger txCost = maybeTransactionReceipt.get().getGasUsed().multiply(gasPrice);
|
||||
|
||||
final BigInteger authorizerBalance = besuNode.execute(ethTransactions.getBalance(authorizer));
|
||||
|
||||
// The remaining balance of the authorizer should the gas limit multiplied by the gas price
|
||||
// minus the transaction cost.
|
||||
// The following executes this calculation in reverse.
|
||||
assertThat(GAS_LIMIT).isEqualTo(authorizerBalance.add(txCost).divide(gasPrice).longValue());
|
||||
|
||||
// The other accounts balance should be the initial 9000 ETH balance from the authorizer minus
|
||||
// the remaining balance of the authorizer and minus the transaction cost
|
||||
cluster.verify(
|
||||
otherAccount.balanceEquals(
|
||||
Amount.wei(
|
||||
new BigInteger("90000000000000000000000")
|
||||
.subtract(authorizerBalance)
|
||||
.subtract(txCost))));
|
||||
}
|
||||
}
|
||||
@@ -413,7 +413,7 @@ public class TransactionPoolOptionsTest
|
||||
@Test
|
||||
public void maxPrioritizedTxsPerTypeWrongTxType() {
|
||||
internalTestFailure(
|
||||
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB, SET_CODE] (case-insensitive) but was 'WRONG_TYPE'",
|
||||
"Invalid value for option '--tx-pool-max-prioritized-by-type' (MAP<TYPE,INTEGER>): expected one of [FRONTIER, ACCESS_LIST, EIP1559, BLOB, DELEGATE_CODE] (case-insensitive) but was 'WRONG_TYPE'",
|
||||
"--tx-pool-max-prioritized-by-type",
|
||||
"WRONG_TYPE=1");
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class SECPSignature {
|
||||
* @param s the s
|
||||
* @param recId the rec id
|
||||
*/
|
||||
SECPSignature(final BigInteger r, final BigInteger s, final byte recId) {
|
||||
public SECPSignature(final BigInteger r, final BigInteger s, final byte recId) {
|
||||
this.r = r;
|
||||
this.s = s;
|
||||
this.recId = recId;
|
||||
|
||||
@@ -20,10 +20,13 @@ import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* SetCodeAuthorization is a data structure that represents the authorization to set code on a EOA
|
||||
* account.
|
||||
* CodeDelegation is a data structure that represents the authorization to delegate code of an EOA
|
||||
* account to another account.
|
||||
*/
|
||||
public interface SetCodeAuthorization {
|
||||
public interface CodeDelegation {
|
||||
/** The cost of delegating code on an existing account. */
|
||||
long PER_AUTH_BASE_COST = 2_500L;
|
||||
|
||||
/**
|
||||
* Return the chain id.
|
||||
*
|
||||
@@ -53,11 +56,11 @@ public interface SetCodeAuthorization {
|
||||
Optional<Address> authorizer();
|
||||
|
||||
/**
|
||||
* Return a valid nonce or empty otherwise. A nonce is valid if the size of the list is exactly 1
|
||||
* Return the nonce
|
||||
*
|
||||
* @return all the optional nonce
|
||||
* @return the nonce
|
||||
*/
|
||||
Optional<Long> nonce();
|
||||
long nonce();
|
||||
|
||||
/**
|
||||
* Return the recovery id.
|
||||
@@ -236,16 +236,16 @@ public interface Transaction {
|
||||
int getSize();
|
||||
|
||||
/**
|
||||
* Returns the set code transaction payload if this transaction is a 7702 transaction.
|
||||
* Returns the code delegations if this transaction is a 7702 transaction.
|
||||
*
|
||||
* @return the set code transaction payloads
|
||||
* @return the code delegations
|
||||
*/
|
||||
Optional<List<SetCodeAuthorization>> getAuthorizationList();
|
||||
Optional<List<CodeDelegation>> getCodeDelegationList();
|
||||
|
||||
/**
|
||||
* Returns the size of the authorization list.
|
||||
*
|
||||
* @return the size of the authorization list
|
||||
*/
|
||||
int authorizationListSize();
|
||||
int codeDelegationListSize();
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ public enum TransactionType {
|
||||
/** Blob transaction type. */
|
||||
BLOB(0x03),
|
||||
/** Eip7702 transaction type. */
|
||||
SET_CODE(0x04);
|
||||
DELEGATE_CODE(0x04);
|
||||
|
||||
private static final Set<TransactionType> ACCESS_LIST_SUPPORTED_TRANSACTION_TYPES =
|
||||
Set.of(ACCESS_LIST, EIP1559, BLOB, SET_CODE);
|
||||
Set.of(ACCESS_LIST, EIP1559, BLOB, DELEGATE_CODE);
|
||||
|
||||
private static final EnumSet<TransactionType> LEGACY_FEE_MARKET_TRANSACTION_TYPES =
|
||||
EnumSet.of(TransactionType.FRONTIER, TransactionType.ACCESS_LIST);
|
||||
@@ -86,7 +86,7 @@ public enum TransactionType {
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionType.EIP1559,
|
||||
TransactionType.BLOB,
|
||||
TransactionType.SET_CODE
|
||||
TransactionType.DELEGATE_CODE
|
||||
})
|
||||
.filter(transactionType -> transactionType.typeValue == serializedTypeValue)
|
||||
.findFirst()
|
||||
@@ -132,12 +132,21 @@ public enum TransactionType {
|
||||
return this.equals(BLOB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does transaction type support delegate code.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean supportsDelegateCode() {
|
||||
return this.equals(DELEGATE_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does transaction type require code.
|
||||
*
|
||||
* @return the boolean
|
||||
*/
|
||||
public boolean requiresSetCode() {
|
||||
return this.equals(SET_CODE);
|
||||
public boolean requiresCodeDelegation() {
|
||||
return this.equals(DELEGATE_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results;
|
||||
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -94,7 +94,7 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
private final List<VersionedHash> versionedHashes;
|
||||
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
private final List<SetCodeAuthorization> authorizationList;
|
||||
private final List<CodeDelegation> authorizationList;
|
||||
|
||||
public TransactionCompleteResult(final TransactionWithMetadata tx) {
|
||||
final Transaction transaction = tx.getTransaction();
|
||||
@@ -131,7 +131,7 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
this.v =
|
||||
(transactionType == TransactionType.ACCESS_LIST
|
||||
|| transactionType == TransactionType.EIP1559)
|
||||
|| transactionType == TransactionType.SET_CODE
|
||||
|| transactionType == TransactionType.DELEGATE_CODE
|
||||
? Quantity.create(transaction.getYParity())
|
||||
: null;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
this.r = Quantity.create(transaction.getR());
|
||||
this.s = Quantity.create(transaction.getS());
|
||||
this.versionedHashes = transaction.getVersionedHashes().orElse(null);
|
||||
this.authorizationList = transaction.getAuthorizationList().orElse(null);
|
||||
this.authorizationList = transaction.getCodeDelegationList().orElse(null);
|
||||
}
|
||||
|
||||
@JsonGetter(value = "accessList")
|
||||
@@ -255,7 +255,7 @@ public class TransactionCompleteResult implements TransactionResult {
|
||||
}
|
||||
|
||||
@JsonGetter(value = "authorizationList")
|
||||
public List<SetCodeAuthorization> getAuthorizationList() {
|
||||
public List<CodeDelegation> getAuthorizationList() {
|
||||
return authorizationList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,10 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.SetCodeTransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationEncoder;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -33,7 +32,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.base.Suppliers;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetCodeAuthorization {
|
||||
public class CodeDelegation implements org.hyperledger.besu.datatypes.CodeDelegation {
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
|
||||
@@ -41,7 +40,7 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
|
||||
private final BigInteger chainId;
|
||||
private final Address address;
|
||||
private final Optional<Long> nonce;
|
||||
private final long nonce;
|
||||
private final SECPSignature signature;
|
||||
private Optional<Address> authorizer = Optional.empty();
|
||||
private boolean isAuthorityComputed = false;
|
||||
@@ -51,13 +50,13 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
*
|
||||
* @param chainId can be either the current chain id or zero
|
||||
* @param address the address from which the code will be set into the EOA account
|
||||
* @param nonce an optional nonce after which this auth expires
|
||||
* @param nonce the nonce after which this auth expires
|
||||
* @param signature the signature of the EOA account which will be used to set the code
|
||||
*/
|
||||
public SetCodeAuthorization(
|
||||
public CodeDelegation(
|
||||
final BigInteger chainId,
|
||||
final Address address,
|
||||
final Optional<Long> nonce,
|
||||
final long nonce,
|
||||
final SECPSignature signature) {
|
||||
this.chainId = chainId;
|
||||
this.address = address;
|
||||
@@ -66,29 +65,26 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
}
|
||||
|
||||
/**
|
||||
* Create access list entry.
|
||||
* Create code delegation.
|
||||
*
|
||||
* @param chainId can be either the current chain id or zero
|
||||
* @param address the address from which the code will be set into the EOA account
|
||||
* @param nonces the list of nonces
|
||||
* @param nonce the nonce
|
||||
* @param v the recovery id
|
||||
* @param r the r value of the signature
|
||||
* @param s the s value of the signature
|
||||
* @return SetCodeTransactionEntry
|
||||
* @return CodeDelegation
|
||||
*/
|
||||
@JsonCreator
|
||||
public static org.hyperledger.besu.datatypes.SetCodeAuthorization createSetCodeAuthorizationEntry(
|
||||
public static org.hyperledger.besu.datatypes.CodeDelegation createCodeDelegation(
|
||||
@JsonProperty("chainId") final BigInteger chainId,
|
||||
@JsonProperty("address") final Address address,
|
||||
@JsonProperty("nonce") final List<Long> nonces,
|
||||
@JsonProperty("nonce") final long nonce,
|
||||
@JsonProperty("v") final byte v,
|
||||
@JsonProperty("r") final BigInteger r,
|
||||
@JsonProperty("s") final BigInteger s) {
|
||||
return new SetCodeAuthorization(
|
||||
chainId,
|
||||
address,
|
||||
Optional.ofNullable(nonces.get(0)),
|
||||
SIGNATURE_ALGORITHM.get().createSignature(r, s, v));
|
||||
return new CodeDelegation(
|
||||
chainId, address, nonce, SIGNATURE_ALGORITHM.get().createSignature(r, s, v));
|
||||
}
|
||||
|
||||
@JsonProperty("chainId")
|
||||
@@ -120,7 +116,7 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Long> nonce() {
|
||||
public long nonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
@@ -144,30 +140,38 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
|
||||
private Optional<Address> computeAuthority() {
|
||||
BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput();
|
||||
SetCodeTransactionEncoder.encodeSingleSetCodeWithoutSignature(this, rlpOutput);
|
||||
CodeDelegationEncoder.encodeSingleCodeDelegationWithoutSignature(this, rlpOutput);
|
||||
|
||||
final Hash hash = Hash.hash(Bytes.concatenate(MAGIC, rlpOutput.encoded()));
|
||||
|
||||
return SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.recoverPublicKeyFromSignature(hash, signature)
|
||||
.map(Address::extract);
|
||||
Optional<Address> authorityAddress;
|
||||
try {
|
||||
authorityAddress =
|
||||
SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.recoverPublicKeyFromSignature(hash, signature)
|
||||
.map(Address::extract);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
authorityAddress = Optional.empty();
|
||||
}
|
||||
|
||||
return authorityAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create set code authorization with a builder.
|
||||
* Create a code delegation authorization with a builder.
|
||||
*
|
||||
* @return SetCodeAuthorization.Builder
|
||||
* @return CodeDelegation.Builder
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/** Builder for SetCodeAuthorization. */
|
||||
/** Builder for CodeDelegation authorizations. */
|
||||
public static class Builder {
|
||||
private BigInteger chainId = BigInteger.ZERO;
|
||||
private Address address;
|
||||
private Optional<Long> nonce = Optional.empty();
|
||||
private Long nonce;
|
||||
private SECPSignature signature;
|
||||
|
||||
/** Create a new builder. */
|
||||
@@ -196,12 +200,12 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the optional nonce.
|
||||
* Set the nonce.
|
||||
*
|
||||
* @param nonce the optional nonce.
|
||||
* @param nonce the nonce.
|
||||
* @return this builder
|
||||
*/
|
||||
public Builder nonces(final Optional<Long> nonce) {
|
||||
public Builder nonce(final long nonce) {
|
||||
this.nonce = nonce;
|
||||
return this;
|
||||
}
|
||||
@@ -221,16 +225,14 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
* Sign the authorization with the given key pair and return the authorization.
|
||||
*
|
||||
* @param keyPair the key pair
|
||||
* @return SetCodeAuthorization
|
||||
* @return CodeDelegation
|
||||
*/
|
||||
public org.hyperledger.besu.datatypes.SetCodeAuthorization signAndBuild(final KeyPair keyPair) {
|
||||
public org.hyperledger.besu.datatypes.CodeDelegation signAndBuild(final KeyPair keyPair) {
|
||||
final BytesValueRLPOutput output = new BytesValueRLPOutput();
|
||||
output.startList();
|
||||
output.writeBigIntegerScalar(chainId);
|
||||
output.writeBytes(address);
|
||||
output.startList();
|
||||
nonce.ifPresent(output::writeLongScalar);
|
||||
output.endList();
|
||||
output.writeLongScalar(nonce);
|
||||
output.endList();
|
||||
|
||||
signature(
|
||||
@@ -243,18 +245,22 @@ public class SetCodeAuthorization implements org.hyperledger.besu.datatypes.SetC
|
||||
/**
|
||||
* Build the authorization.
|
||||
*
|
||||
* @return SetCodeAuthorization
|
||||
* @return CodeDelegation
|
||||
*/
|
||||
public org.hyperledger.besu.datatypes.SetCodeAuthorization build() {
|
||||
public org.hyperledger.besu.datatypes.CodeDelegation build() {
|
||||
if (address == null) {
|
||||
throw new IllegalStateException("Address must be set");
|
||||
}
|
||||
|
||||
if (nonce == null) {
|
||||
throw new IllegalStateException("Nonce must be set");
|
||||
}
|
||||
|
||||
if (signature == null) {
|
||||
throw new IllegalStateException("Signature must be set");
|
||||
}
|
||||
|
||||
return new SetCodeAuthorization(chainId, address, nonce, signature);
|
||||
return new CodeDelegation(chainId, address, nonce, signature);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,18 +28,18 @@ import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.KZGProof;
|
||||
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.datatypes.Sha256Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.BlobTransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.CodeDelegationEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.EncodingContext;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.SetCodeTransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionDecoder;
|
||||
import org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
@@ -124,7 +124,7 @@ public class Transaction
|
||||
private final Optional<List<VersionedHash>> versionedHashes;
|
||||
|
||||
private final Optional<BlobsWithCommitments> blobsWithCommitments;
|
||||
private final Optional<List<SetCodeAuthorization>> maybeAuthorizationList;
|
||||
private final Optional<List<CodeDelegation>> maybeCodeDelegationList;
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
@@ -181,7 +181,7 @@ public class Transaction
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<VersionedHash>> versionedHashes,
|
||||
final Optional<BlobsWithCommitments> blobsWithCommitments,
|
||||
final Optional<List<SetCodeAuthorization>> maybeAuthorizationList) {
|
||||
final Optional<List<CodeDelegation>> maybeCodeDelegationList) {
|
||||
|
||||
if (!forCopy) {
|
||||
if (transactionType.requiresChainId()) {
|
||||
@@ -218,10 +218,10 @@ public class Transaction
|
||||
maxFeePerBlobGas.isPresent(), "Must specify max fee per blob gas for blob transaction");
|
||||
}
|
||||
|
||||
if (transactionType.requiresSetCode()) {
|
||||
if (transactionType.requiresCodeDelegation()) {
|
||||
checkArgument(
|
||||
maybeAuthorizationList.isPresent(),
|
||||
"Must specify set code transaction payload for set code transaction");
|
||||
maybeCodeDelegationList.isPresent(),
|
||||
"Must specify code delegation authorizations for code delegation transaction");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ public class Transaction
|
||||
this.chainId = chainId;
|
||||
this.versionedHashes = versionedHashes;
|
||||
this.blobsWithCommitments = blobsWithCommitments;
|
||||
this.maybeAuthorizationList = maybeAuthorizationList;
|
||||
this.maybeCodeDelegationList = maybeCodeDelegationList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -473,7 +473,7 @@ public class Transaction
|
||||
payload,
|
||||
maybeAccessList,
|
||||
versionedHashes.orElse(null),
|
||||
maybeAuthorizationList,
|
||||
maybeCodeDelegationList,
|
||||
chainId);
|
||||
}
|
||||
return hashNoSignature;
|
||||
@@ -681,13 +681,13 @@ public class Transaction
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<List<SetCodeAuthorization>> getAuthorizationList() {
|
||||
return maybeAuthorizationList;
|
||||
public Optional<List<CodeDelegation>> getCodeDelegationList() {
|
||||
return maybeCodeDelegationList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int authorizationListSize() {
|
||||
return maybeAuthorizationList.map(List::size).orElse(0);
|
||||
public int codeDelegationListSize() {
|
||||
return maybeCodeDelegationList.map(List::size).orElse(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,7 +714,7 @@ public class Transaction
|
||||
final Bytes payload,
|
||||
final Optional<List<AccessListEntry>> accessList,
|
||||
final List<VersionedHash> versionedHashes,
|
||||
final Optional<List<SetCodeAuthorization>> authorizationList,
|
||||
final Optional<List<CodeDelegation>> codeDelegationList,
|
||||
final Optional<BigInteger> chainId) {
|
||||
if (transactionType.requiresChainId()) {
|
||||
checkArgument(chainId.isPresent(), "Transaction type %s requires chainId", transactionType);
|
||||
@@ -759,8 +759,8 @@ public class Transaction
|
||||
new IllegalStateException(
|
||||
"Developer error: the transaction should be guaranteed to have an access list here")),
|
||||
chainId);
|
||||
case SET_CODE ->
|
||||
setCodePreimage(
|
||||
case DELEGATE_CODE ->
|
||||
codeDelegationPreimage(
|
||||
nonce,
|
||||
maxPriorityFeePerGas,
|
||||
maxFeePerGas,
|
||||
@@ -770,10 +770,10 @@ public class Transaction
|
||||
payload,
|
||||
chainId,
|
||||
accessList,
|
||||
authorizationList.orElseThrow(
|
||||
codeDelegationList.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
"Developer error: the transaction should be guaranteed to have a set code payload here")));
|
||||
"Developer error: the transaction should be guaranteed to have a code delegations here")));
|
||||
};
|
||||
return keccak256(preimage);
|
||||
}
|
||||
@@ -911,7 +911,7 @@ public class Transaction
|
||||
return Bytes.concatenate(Bytes.of(TransactionType.ACCESS_LIST.getSerializedType()), encode);
|
||||
}
|
||||
|
||||
private static Bytes setCodePreimage(
|
||||
private static Bytes codeDelegationPreimage(
|
||||
final long nonce,
|
||||
final Wei maxPriorityFeePerGas,
|
||||
final Wei maxFeePerGas,
|
||||
@@ -921,7 +921,7 @@ public class Transaction
|
||||
final Bytes payload,
|
||||
final Optional<BigInteger> chainId,
|
||||
final Optional<List<AccessListEntry>> accessList,
|
||||
final List<SetCodeAuthorization> authorizationList) {
|
||||
final List<CodeDelegation> authorizationList) {
|
||||
final Bytes encoded =
|
||||
RLP.encode(
|
||||
rlpOutput -> {
|
||||
@@ -937,10 +937,10 @@ public class Transaction
|
||||
chainId,
|
||||
accessList,
|
||||
rlpOutput);
|
||||
SetCodeTransactionEncoder.encodeSetCodeInner(authorizationList, rlpOutput);
|
||||
CodeDelegationEncoder.encodeCodeDelegationInner(authorizationList, rlpOutput);
|
||||
rlpOutput.endList();
|
||||
});
|
||||
return Bytes.concatenate(Bytes.of(TransactionType.SET_CODE.getSerializedType()), encoded);
|
||||
return Bytes.concatenate(Bytes.of(TransactionType.DELEGATE_CODE.getSerializedType()), encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1111,7 +1111,7 @@ public class Transaction
|
||||
chainId,
|
||||
detachedVersionedHashes,
|
||||
detachedBlobsWithCommitments,
|
||||
maybeAuthorizationList);
|
||||
maybeCodeDelegationList);
|
||||
|
||||
// copy also the computed fields, to avoid to recompute them
|
||||
copiedTx.sender = this.sender;
|
||||
@@ -1179,7 +1179,7 @@ public class Transaction
|
||||
protected Optional<BigInteger> v = Optional.empty();
|
||||
protected List<VersionedHash> versionedHashes = null;
|
||||
private BlobsWithCommitments blobsWithCommitments;
|
||||
protected Optional<List<SetCodeAuthorization>> setCodeTransactionPayloads = Optional.empty();
|
||||
protected Optional<List<CodeDelegation>> codeDelegationAuthorizations = Optional.empty();
|
||||
|
||||
public Builder copiedFrom(final Transaction toCopy) {
|
||||
this.transactionType = toCopy.transactionType;
|
||||
@@ -1198,7 +1198,7 @@ public class Transaction
|
||||
this.chainId = toCopy.chainId;
|
||||
this.versionedHashes = toCopy.versionedHashes.orElse(null);
|
||||
this.blobsWithCommitments = toCopy.blobsWithCommitments.orElse(null);
|
||||
this.setCodeTransactionPayloads = toCopy.maybeAuthorizationList;
|
||||
this.codeDelegationAuthorizations = toCopy.maybeCodeDelegationList;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1292,8 +1292,8 @@ public class Transaction
|
||||
transactionType = TransactionType.EIP1559;
|
||||
} else if (accessList.isPresent()) {
|
||||
transactionType = TransactionType.ACCESS_LIST;
|
||||
} else if (setCodeTransactionPayloads.isPresent()) {
|
||||
transactionType = TransactionType.SET_CODE;
|
||||
} else if (codeDelegationAuthorizations.isPresent()) {
|
||||
transactionType = TransactionType.DELEGATE_CODE;
|
||||
} else {
|
||||
transactionType = TransactionType.FRONTIER;
|
||||
}
|
||||
@@ -1324,7 +1324,7 @@ public class Transaction
|
||||
chainId,
|
||||
Optional.ofNullable(versionedHashes),
|
||||
Optional.ofNullable(blobsWithCommitments),
|
||||
setCodeTransactionPayloads);
|
||||
codeDelegationAuthorizations);
|
||||
}
|
||||
|
||||
public Transaction signAndBuild(final KeyPair keys) {
|
||||
@@ -1351,7 +1351,7 @@ public class Transaction
|
||||
payload,
|
||||
accessList,
|
||||
versionedHashes,
|
||||
setCodeTransactionPayloads,
|
||||
codeDelegationAuthorizations,
|
||||
chainId),
|
||||
keys);
|
||||
}
|
||||
@@ -1376,9 +1376,8 @@ public class Transaction
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCodeTransactionPayloads(
|
||||
final List<SetCodeAuthorization> setCodeTransactionEntries) {
|
||||
this.setCodeTransactionPayloads = Optional.ofNullable(setCodeTransactionEntries);
|
||||
public Builder codeDelegations(final List<CodeDelegation> codeDelegations) {
|
||||
this.codeDelegationAuthorizations = Optional.ofNullable(codeDelegations);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.hyperledger.besu.ethereum.core.encoding;
|
||||
import static org.hyperledger.besu.ethereum.core.encoding.AccessListTransactionEncoder.writeAccessList;
|
||||
import static org.hyperledger.besu.ethereum.core.encoding.TransactionEncoder.writeSignatureAndRecoveryId;
|
||||
|
||||
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
|
||||
|
||||
@@ -25,28 +25,28 @@ import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class SetCodeTransactionEncoder {
|
||||
public class CodeDelegationEncoder {
|
||||
|
||||
private SetCodeTransactionEncoder() {
|
||||
private CodeDelegationEncoder() {
|
||||
// private constructor
|
||||
}
|
||||
|
||||
public static void encodeSetCodeInner(
|
||||
final List<SetCodeAuthorization> payloads, final RLPOutput rlpOutput) {
|
||||
public static void encodeCodeDelegationInner(
|
||||
final List<CodeDelegation> payloads, final RLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
payloads.forEach(payload -> encodeSingleSetCode(payload, rlpOutput));
|
||||
payloads.forEach(payload -> encodeSingleCodeDelegation(payload, rlpOutput));
|
||||
rlpOutput.endList();
|
||||
}
|
||||
|
||||
public static void encodeSingleSetCodeWithoutSignature(
|
||||
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
|
||||
public static void encodeSingleCodeDelegationWithoutSignature(
|
||||
final CodeDelegation payload, final RLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
encodeAuthorizationDetails(payload, rlpOutput);
|
||||
rlpOutput.endList();
|
||||
}
|
||||
|
||||
public static void encodeSingleSetCode(
|
||||
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
|
||||
public static void encodeSingleCodeDelegation(
|
||||
final CodeDelegation payload, final RLPOutput rlpOutput) {
|
||||
rlpOutput.startList();
|
||||
encodeAuthorizationDetails(payload, rlpOutput);
|
||||
rlpOutput.writeIntScalar(payload.signature().getRecId());
|
||||
@@ -56,12 +56,10 @@ public class SetCodeTransactionEncoder {
|
||||
}
|
||||
|
||||
private static void encodeAuthorizationDetails(
|
||||
final SetCodeAuthorization payload, final RLPOutput rlpOutput) {
|
||||
final CodeDelegation payload, final RLPOutput rlpOutput) {
|
||||
rlpOutput.writeBigIntegerScalar(payload.chainId());
|
||||
rlpOutput.writeBytes(payload.address().copy());
|
||||
rlpOutput.startList();
|
||||
payload.nonce().ifPresent(rlpOutput::writeLongScalar);
|
||||
rlpOutput.endList();
|
||||
rlpOutput.writeLongScalar(payload.nonce());
|
||||
}
|
||||
|
||||
public static void encode(final Transaction transaction, final RLPOutput out) {
|
||||
@@ -75,13 +73,13 @@ public class SetCodeTransactionEncoder {
|
||||
out.writeUInt256Scalar(transaction.getValue());
|
||||
out.writeBytes(transaction.getPayload());
|
||||
writeAccessList(out, transaction.getAccessList());
|
||||
encodeSetCodeInner(
|
||||
encodeCodeDelegationInner(
|
||||
transaction
|
||||
.getAuthorizationList()
|
||||
.getCodeDelegationList()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(
|
||||
"Developer error: the transaction should be guaranteed to have a set code payload here")),
|
||||
"Developer error: the transaction should be guaranteed to have a code delegation authorizations here")),
|
||||
out);
|
||||
writeSignatureAndRecoveryId(transaction, out);
|
||||
out.endList();
|
||||
@@ -19,23 +19,22 @@ import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPInput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
public class SetCodeTransactionDecoder {
|
||||
public class CodeDelegationTransactionDecoder {
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
|
||||
private SetCodeTransactionDecoder() {
|
||||
private CodeDelegationTransactionDecoder() {
|
||||
// private constructor
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ public class SetCodeTransactionDecoder {
|
||||
final BigInteger chainId = input.readBigIntegerScalar();
|
||||
final Transaction.Builder builder =
|
||||
Transaction.builder()
|
||||
.type(TransactionType.SET_CODE)
|
||||
.type(TransactionType.DELEGATE_CODE)
|
||||
.chainId(chainId)
|
||||
.nonce(input.readLongScalar())
|
||||
.maxPriorityFeePerGas(Wei.of(input.readUInt256Scalar()))
|
||||
@@ -64,10 +63,7 @@ public class SetCodeTransactionDecoder {
|
||||
accessListEntryRLPInput.leaveList();
|
||||
return accessListEntry;
|
||||
}))
|
||||
.setCodeTransactionPayloads(
|
||||
input.readList(
|
||||
setCodeTransactionPayloadsRLPInput ->
|
||||
decodeInnerPayload(setCodeTransactionPayloadsRLPInput)));
|
||||
.codeDelegations(input.readList(CodeDelegationTransactionDecoder::decodeInnerPayload));
|
||||
|
||||
final byte recId = (byte) input.readUnsignedByteScalar();
|
||||
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
|
||||
@@ -75,33 +71,15 @@ public class SetCodeTransactionDecoder {
|
||||
|
||||
input.leaveList();
|
||||
|
||||
final Transaction transaction =
|
||||
builder.signature(SIGNATURE_ALGORITHM.get().createSignature(r, s, recId)).build();
|
||||
|
||||
return transaction;
|
||||
return builder.signature(SIGNATURE_ALGORITHM.get().createSignature(r, s, recId)).build();
|
||||
}
|
||||
|
||||
public static org.hyperledger.besu.datatypes.SetCodeAuthorization decodeInnerPayload(
|
||||
final RLPInput input) {
|
||||
public static CodeDelegation decodeInnerPayload(final RLPInput input) {
|
||||
input.enterList();
|
||||
|
||||
final BigInteger chainId = input.readBigIntegerScalar();
|
||||
final Address address = Address.wrap(input.readBytes());
|
||||
|
||||
Optional<Long> nonce = Optional.empty();
|
||||
|
||||
if (!input.nextIsList()) {
|
||||
throw new IllegalArgumentException("Optional nonce must be an list, but isn't");
|
||||
}
|
||||
|
||||
final long noncesSize = input.nextSize();
|
||||
|
||||
input.enterList();
|
||||
if (noncesSize == 1) {
|
||||
nonce = Optional.ofNullable(input.readLongScalar());
|
||||
} else if (noncesSize > 1) {
|
||||
throw new IllegalArgumentException("Nonce list may only have 1 member, if any");
|
||||
}
|
||||
input.leaveList();
|
||||
final long nonce = input.readLongScalar();
|
||||
|
||||
final byte yParity = (byte) input.readUnsignedByteScalar();
|
||||
final BigInteger r = input.readUInt256Scalar().toUnsignedBigInteger();
|
||||
@@ -111,6 +89,7 @@ public class SetCodeTransactionDecoder {
|
||||
|
||||
final SECPSignature signature = SIGNATURE_ALGORITHM.get().createSignature(r, s, yParity);
|
||||
|
||||
return new SetCodeAuthorization(chainId, address, nonce, signature);
|
||||
return new org.hyperledger.besu.ethereum.core.CodeDelegation(
|
||||
chainId, address, nonce, signature);
|
||||
}
|
||||
}
|
||||
@@ -41,8 +41,8 @@ public class TransactionDecoder {
|
||||
EIP1559TransactionDecoder::decode,
|
||||
TransactionType.BLOB,
|
||||
BlobTransactionDecoder::decode,
|
||||
TransactionType.SET_CODE,
|
||||
SetCodeTransactionDecoder::decode);
|
||||
TransactionType.DELEGATE_CODE,
|
||||
CodeDelegationTransactionDecoder::decode);
|
||||
|
||||
private static final ImmutableMap<TransactionType, Decoder> POOLED_TRANSACTION_DECODERS =
|
||||
ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionDecoder::decode);
|
||||
|
||||
@@ -40,8 +40,8 @@ public class TransactionEncoder {
|
||||
EIP1559TransactionEncoder::encode,
|
||||
TransactionType.BLOB,
|
||||
BlobTransactionEncoder::encode,
|
||||
TransactionType.SET_CODE,
|
||||
SetCodeTransactionEncoder::encode);
|
||||
TransactionType.DELEGATE_CODE,
|
||||
CodeDelegationEncoder::encode);
|
||||
|
||||
private static final ImmutableMap<TransactionType, Encoder> POOLED_TRANSACTION_ENCODERS =
|
||||
ImmutableMap.of(TransactionType.BLOB, BlobPooledTransactionEncoder::encode);
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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 org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.AccountState;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.worldstate.EVMWorldUpdater;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AuthorityProcessor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuthorityProcessor.class);
|
||||
|
||||
private final Optional<BigInteger> maybeChainId;
|
||||
|
||||
public AuthorityProcessor(final Optional<BigInteger> maybeChainId) {
|
||||
this.maybeChainId = maybeChainId;
|
||||
}
|
||||
|
||||
public void addContractToAuthority(
|
||||
final EVMWorldUpdater evmWorldUpdater, final Transaction transaction) {
|
||||
transaction
|
||||
.getAuthorizationList()
|
||||
.get()
|
||||
.forEach(
|
||||
payload ->
|
||||
payload
|
||||
.authorizer()
|
||||
.ifPresent(
|
||||
authorityAddress -> {
|
||||
LOG.trace("Set code authority: {}", authorityAddress);
|
||||
|
||||
if (maybeChainId.isPresent()
|
||||
&& !payload.chainId().equals(BigInteger.ZERO)
|
||||
&& !maybeChainId.get().equals(payload.chainId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<MutableAccount> maybeAccount =
|
||||
Optional.ofNullable(evmWorldUpdater.getAccount(authorityAddress));
|
||||
final long accountNonce =
|
||||
maybeAccount.map(AccountState::getNonce).orElse(0L);
|
||||
|
||||
if (payload.nonce().isPresent()
|
||||
&& !payload.nonce().get().equals(accountNonce)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evmWorldUpdater
|
||||
.authorizedCodeService()
|
||||
.hasAuthorizedCode(authorityAddress)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<Account> codeAccount =
|
||||
Optional.ofNullable(evmWorldUpdater.get(payload.address()));
|
||||
final Bytes code;
|
||||
if (codeAccount.isPresent()) {
|
||||
code = codeAccount.get().getCode();
|
||||
} else {
|
||||
code = Bytes.EMPTY;
|
||||
}
|
||||
|
||||
evmWorldUpdater
|
||||
.authorizedCodeService()
|
||||
.addAuthorizedCode(authorityAddress, code);
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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 org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.core.CodeDelegation;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.worldstate.EVMWorldUpdater;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class CodeDelegationProcessor {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CodeDelegationProcessor.class);
|
||||
|
||||
private final Optional<BigInteger> maybeChainId;
|
||||
|
||||
public CodeDelegationProcessor(final Optional<BigInteger> maybeChainId) {
|
||||
this.maybeChainId = maybeChainId;
|
||||
}
|
||||
|
||||
/**
|
||||
* At the start of executing the transaction, after incrementing the sender’s nonce, for each
|
||||
* authorization we do the following:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Verify the chain id is either 0 or the chain's current ID.
|
||||
* <li>`authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, nonce])), y_parity, r, s]`
|
||||
* <li>Add `authority` to `accessed_addresses` (as defined in [EIP-2929](./eip-2929.md).)
|
||||
* <li>Verify the code of `authority` is either empty or already delegated.
|
||||
* <li>Verify the nonce of `authority` is equal to `nonce`.
|
||||
* <li>Add `PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST` gas to the global refund counter if
|
||||
* `authority` exists in the trie.
|
||||
* <li>Set the code of `authority` to be `0xef0100 || address`. This is a delegation
|
||||
* designation.
|
||||
* <li>Increase the nonce of `authority` by one.
|
||||
* </ol>
|
||||
*
|
||||
* @param evmWorldUpdater The world state updater which is aware of code delegation.
|
||||
* @param transaction The transaction being processed.
|
||||
* @return The result of the code delegation processing.
|
||||
*/
|
||||
public CodeDelegationResult process(
|
||||
final EVMWorldUpdater evmWorldUpdater, final Transaction transaction) {
|
||||
final CodeDelegationResult result = new CodeDelegationResult();
|
||||
|
||||
transaction
|
||||
.getCodeDelegationList()
|
||||
.get()
|
||||
.forEach(
|
||||
codeDelegation ->
|
||||
processAuthorization(
|
||||
evmWorldUpdater,
|
||||
(org.hyperledger.besu.ethereum.core.CodeDelegation) codeDelegation,
|
||||
result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void processAuthorization(
|
||||
final EVMWorldUpdater evmWorldUpdater,
|
||||
final CodeDelegation codeDelegation,
|
||||
final CodeDelegationResult result) {
|
||||
LOG.trace("Processing code delegation: {}", codeDelegation);
|
||||
|
||||
if (maybeChainId.isPresent()
|
||||
&& !codeDelegation.chainId().equals(BigInteger.ZERO)
|
||||
&& !maybeChainId.get().equals(codeDelegation.chainId())) {
|
||||
LOG.trace(
|
||||
"Invalid chain id for code delegation. Expected: {}, Actual: {}",
|
||||
maybeChainId.get(),
|
||||
codeDelegation.chainId());
|
||||
return;
|
||||
}
|
||||
|
||||
final Optional<Address> authorizer = codeDelegation.authorizer();
|
||||
if (authorizer.isEmpty()) {
|
||||
LOG.trace("Invalid signature for code delegation");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.trace("Set code delegation for authority: {}", authorizer.get());
|
||||
|
||||
final Optional<MutableAccount> maybeAuthorityAccount =
|
||||
Optional.ofNullable(evmWorldUpdater.getAccount(authorizer.get()));
|
||||
|
||||
result.addAccessedDelegatorAddress(authorizer.get());
|
||||
|
||||
MutableAccount authority;
|
||||
boolean authorityDoesAlreadyExist = false;
|
||||
if (maybeAuthorityAccount.isEmpty()) {
|
||||
authority = evmWorldUpdater.createAccount(authorizer.get());
|
||||
} else {
|
||||
authority = maybeAuthorityAccount.get();
|
||||
|
||||
if (!evmWorldUpdater.authorizedCodeService().canSetDelegatedCode(authority)) {
|
||||
return;
|
||||
}
|
||||
|
||||
authorityDoesAlreadyExist = true;
|
||||
}
|
||||
|
||||
if (codeDelegation.nonce() != authority.getNonce()) {
|
||||
LOG.trace(
|
||||
"Invalid nonce for code delegation. Expected: {}, Actual: {}",
|
||||
authority.getNonce(),
|
||||
codeDelegation.nonce());
|
||||
return;
|
||||
}
|
||||
|
||||
if (authorityDoesAlreadyExist) {
|
||||
result.incremenentAlreadyExistingDelegators();
|
||||
}
|
||||
|
||||
evmWorldUpdater.authorizedCodeService().addDelegatedCode(authority, codeDelegation.address());
|
||||
authority.incrementNonce();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 org.hyperledger.besu.collections.trie.BytesTrieSet;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class CodeDelegationResult {
|
||||
private final Set<Address> accessedDelegatorAddresses = new BytesTrieSet<>(Address.SIZE);
|
||||
private long alreadyExistingDelegators = 0L;
|
||||
|
||||
public void addAccessedDelegatorAddress(final Address address) {
|
||||
accessedDelegatorAddresses.add(address);
|
||||
}
|
||||
|
||||
public void incremenentAlreadyExistingDelegators() {
|
||||
alreadyExistingDelegators += 1;
|
||||
}
|
||||
|
||||
public Set<Address> accessedDelegatorAddresses() {
|
||||
return accessedDelegatorAddresses;
|
||||
}
|
||||
|
||||
public long alreadyExistingDelegators() {
|
||||
return alreadyExistingDelegators;
|
||||
}
|
||||
}
|
||||
@@ -715,7 +715,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
evmConfiguration.evmStackSize(),
|
||||
feeMarket,
|
||||
CoinbaseFeePriceCalculator.eip1559(),
|
||||
new AuthorityProcessor(chainId)))
|
||||
new CodeDelegationProcessor(chainId)))
|
||||
// change to check for max blob gas per block for EIP-4844
|
||||
.transactionValidatorFactoryBuilder(
|
||||
(evm, gasLimitCalculator, feeMarket) ->
|
||||
@@ -813,7 +813,7 @@ public abstract class MainnetProtocolSpecs {
|
||||
TransactionType.ACCESS_LIST,
|
||||
TransactionType.EIP1559,
|
||||
TransactionType.BLOB,
|
||||
TransactionType.SET_CODE),
|
||||
TransactionType.DELEGATE_CODE),
|
||||
evm.getMaxInitcodeSize()))
|
||||
|
||||
// EIP-2935 Blockhash processor
|
||||
|
||||
@@ -81,7 +81,7 @@ public class MainnetTransactionProcessor {
|
||||
protected final FeeMarket feeMarket;
|
||||
private final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
|
||||
|
||||
private final Optional<AuthorityProcessor> maybeAuthorityProcessor;
|
||||
private final Optional<CodeDelegationProcessor> maybeCodeDelegationProcessor;
|
||||
|
||||
public MainnetTransactionProcessor(
|
||||
final GasCalculator gasCalculator,
|
||||
@@ -116,7 +116,7 @@ public class MainnetTransactionProcessor {
|
||||
final int maxStackSize,
|
||||
final FeeMarket feeMarket,
|
||||
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator,
|
||||
final AuthorityProcessor maybeAuthorityProcessor) {
|
||||
final CodeDelegationProcessor maybeCodeDelegationProcessor) {
|
||||
this.gasCalculator = gasCalculator;
|
||||
this.transactionValidatorFactory = transactionValidatorFactory;
|
||||
this.contractCreationProcessor = contractCreationProcessor;
|
||||
@@ -126,7 +126,7 @@ public class MainnetTransactionProcessor {
|
||||
this.maxStackSize = maxStackSize;
|
||||
this.feeMarket = feeMarket;
|
||||
this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator;
|
||||
this.maybeAuthorityProcessor = Optional.ofNullable(maybeAuthorityProcessor);
|
||||
this.maybeCodeDelegationProcessor = Optional.ofNullable(maybeCodeDelegationProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,16 +316,7 @@ public class MainnetTransactionProcessor {
|
||||
|
||||
operationTracer.tracePrepareTransaction(evmWorldUpdater, transaction);
|
||||
|
||||
final Set<Address> addressList = new BytesTrieSet<>(Address.SIZE);
|
||||
|
||||
if (transaction.getAuthorizationList().isPresent()) {
|
||||
if (maybeAuthorityProcessor.isEmpty()) {
|
||||
throw new RuntimeException("Authority processor is required for 7702 transactions");
|
||||
}
|
||||
|
||||
maybeAuthorityProcessor.get().addContractToAuthority(evmWorldUpdater, transaction);
|
||||
addressList.addAll(evmWorldUpdater.authorizedCodeService().getAuthorities());
|
||||
}
|
||||
final Set<Address> warmAddressList = new BytesTrieSet<>(Address.SIZE);
|
||||
|
||||
final long previousNonce = sender.incrementNonce();
|
||||
LOG.trace(
|
||||
@@ -349,6 +340,20 @@ public class MainnetTransactionProcessor {
|
||||
previousBalance,
|
||||
sender.getBalance());
|
||||
|
||||
long codeDelegationRefund = 0L;
|
||||
if (transaction.getCodeDelegationList().isPresent()) {
|
||||
if (maybeCodeDelegationProcessor.isEmpty()) {
|
||||
throw new RuntimeException("Code delegation processor is required for 7702 transactions");
|
||||
}
|
||||
|
||||
final CodeDelegationResult codeDelegationResult =
|
||||
maybeCodeDelegationProcessor.get().process(evmWorldUpdater, transaction);
|
||||
warmAddressList.addAll(codeDelegationResult.accessedDelegatorAddresses());
|
||||
codeDelegationRefund =
|
||||
gasCalculator.calculateDelegateCodeGasRefund(
|
||||
(codeDelegationResult.alreadyExistingDelegators()));
|
||||
}
|
||||
|
||||
final List<AccessListEntry> accessListEntries = transaction.getAccessList().orElse(List.of());
|
||||
// we need to keep a separate hash set of addresses in case they specify no storage.
|
||||
// No-storage is a common pattern, especially for Externally Owned Accounts
|
||||
@@ -356,13 +361,13 @@ public class MainnetTransactionProcessor {
|
||||
int accessListStorageCount = 0;
|
||||
for (final var entry : accessListEntries) {
|
||||
final Address address = entry.address();
|
||||
addressList.add(address);
|
||||
warmAddressList.add(address);
|
||||
final List<Bytes32> storageKeys = entry.storageKeys();
|
||||
storageList.putAll(address, storageKeys);
|
||||
accessListStorageCount += storageKeys.size();
|
||||
}
|
||||
if (warmCoinbase) {
|
||||
addressList.add(miningBeneficiary);
|
||||
warmAddressList.add(miningBeneficiary);
|
||||
}
|
||||
|
||||
final long intrinsicGas =
|
||||
@@ -370,16 +375,17 @@ public class MainnetTransactionProcessor {
|
||||
transaction.getPayload(), transaction.isContractCreation());
|
||||
final long accessListGas =
|
||||
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
|
||||
final long setCodeGas = gasCalculator.setCodeListGasCost(transaction.authorizationListSize());
|
||||
final long codeDelegationGas =
|
||||
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
final long gasAvailable =
|
||||
transaction.getGasLimit() - intrinsicGas - accessListGas - setCodeGas;
|
||||
transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas;
|
||||
LOG.trace(
|
||||
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - setCode)",
|
||||
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)",
|
||||
gasAvailable,
|
||||
transaction.getGasLimit(),
|
||||
intrinsicGas,
|
||||
accessListGas,
|
||||
setCodeGas);
|
||||
codeDelegationGas);
|
||||
|
||||
final WorldUpdater worldUpdater = evmWorldUpdater.updater();
|
||||
final ImmutableMap.Builder<String, Object> contextVariablesBuilder =
|
||||
@@ -409,7 +415,7 @@ public class MainnetTransactionProcessor {
|
||||
.miningBeneficiary(miningBeneficiary)
|
||||
.blockHashLookup(blockHashLookup)
|
||||
.contextVariables(contextVariablesBuilder.build())
|
||||
.accessListWarmAddresses(addressList)
|
||||
.accessListWarmAddresses(warmAddressList)
|
||||
.accessListWarmStorage(storageList);
|
||||
|
||||
if (transaction.getVersionedHashes().isPresent()) {
|
||||
@@ -488,7 +494,8 @@ public class MainnetTransactionProcessor {
|
||||
// after the other so that if it is the same account somehow, we end up with the right result)
|
||||
final long selfDestructRefund =
|
||||
gasCalculator.getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long baseRefundGas = initialFrame.getGasRefund() + selfDestructRefund;
|
||||
final long baseRefundGas =
|
||||
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
|
||||
final long refundedGas = refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas);
|
||||
final Wei refundedWei = transactionGasPrice.multiply(refundedGas);
|
||||
final Wei balancePriorToRefund = sender.getBalance();
|
||||
@@ -528,7 +535,6 @@ public class MainnetTransactionProcessor {
|
||||
|
||||
final var coinbase = evmWorldUpdater.getOrCreate(miningBeneficiary);
|
||||
coinbase.incrementBalance(coinbaseWeiDelta);
|
||||
evmWorldUpdater.authorizedCodeService().resetAuthorities();
|
||||
|
||||
operationTracer.traceEndTransaction(
|
||||
worldUpdater,
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Blob;
|
||||
import org.hyperledger.besu.datatypes.BlobsWithCommitments;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.KZGCommitment;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
@@ -129,9 +130,50 @@ public class MainnetTransactionValidator implements TransactionValidator {
|
||||
transaction.getPayload().size(), maxInitcodeSize));
|
||||
}
|
||||
|
||||
if (transactionType == TransactionType.DELEGATE_CODE) {
|
||||
if (isDelegateCodeEmpty(transaction)) {
|
||||
return ValidationResult.invalid(
|
||||
TransactionInvalidReason.EMPTY_CODE_DELEGATION,
|
||||
"transaction code delegation transactions must have a non-empty code delegation list");
|
||||
}
|
||||
|
||||
final BigInteger halfCurveOrder = SignatureAlgorithmFactory.getInstance().getHalfCurveOrder();
|
||||
final Optional<ValidationResult<TransactionInvalidReason>> validationResult =
|
||||
transaction
|
||||
.getCodeDelegationList()
|
||||
.map(
|
||||
codeDelegations -> {
|
||||
for (CodeDelegation codeDelegation : codeDelegations) {
|
||||
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.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.");
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult.valid();
|
||||
});
|
||||
|
||||
if (validationResult.isPresent() && !validationResult.get().isValid()) {
|
||||
return validationResult.get();
|
||||
}
|
||||
}
|
||||
|
||||
return validateCostAndFee(transaction, baseFee, blobFee, transactionValidationParams);
|
||||
}
|
||||
|
||||
private static boolean isDelegateCodeEmpty(final Transaction transaction) {
|
||||
return transaction.getCodeDelegationList().isEmpty()
|
||||
|| transaction.getCodeDelegationList().get().isEmpty();
|
||||
}
|
||||
|
||||
private ValidationResult<TransactionInvalidReason> validateCostAndFee(
|
||||
final Transaction transaction,
|
||||
final Optional<Wei> maybeBaseFee,
|
||||
@@ -190,7 +232,7 @@ public class MainnetTransactionValidator implements TransactionValidator {
|
||||
gasCalculator.transactionIntrinsicGasCost(
|
||||
transaction.getPayload(), transaction.isContractCreation())
|
||||
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L))
|
||||
+ gasCalculator.setCodeListGasCost(transaction.authorizationListSize());
|
||||
+ gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
|
||||
if (Long.compareUnsigned(intrinsicGasCost, transaction.getGasLimit()) > 0) {
|
||||
return ValidationResult.invalid(
|
||||
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
|
||||
@@ -250,7 +292,8 @@ public class MainnetTransactionValidator implements TransactionValidator {
|
||||
transaction.getNonce(), senderNonce));
|
||||
}
|
||||
|
||||
if (!validationParams.isAllowContractAddressAsSender() && !codeHash.equals(Hash.EMPTY)) {
|
||||
if (!validationParams.isAllowContractAddressAsSender()
|
||||
&& !canSendTransaction(sender, codeHash)) {
|
||||
return ValidationResult.invalid(
|
||||
TransactionInvalidReason.TX_SENDER_NOT_AUTHORIZED,
|
||||
String.format(
|
||||
@@ -261,6 +304,10 @@ public class MainnetTransactionValidator implements TransactionValidator {
|
||||
return ValidationResult.valid();
|
||||
}
|
||||
|
||||
private static boolean canSendTransaction(final Account sender, final Hash codeHash) {
|
||||
return codeHash.equals(Hash.EMPTY) || sender.hasDelegatedCode();
|
||||
}
|
||||
|
||||
private ValidationResult<TransactionInvalidReason> validateTransactionSignature(
|
||||
final Transaction transaction) {
|
||||
if (chainId.isPresent()
|
||||
|
||||
@@ -50,6 +50,7 @@ public enum TransactionInvalidReason {
|
||||
PLUGIN_TX_POOL_VALIDATOR,
|
||||
EXECUTION_HALTED,
|
||||
EOF_CODE_INVALID,
|
||||
EMPTY_CODE_DELEGATION,
|
||||
// Private Transaction Invalid Reasons
|
||||
PRIVATE_TRANSACTION_INVALID,
|
||||
PRIVATE_TRANSACTION_FAILED,
|
||||
|
||||
@@ -376,7 +376,7 @@ public class BlockDataGenerator {
|
||||
case EIP1559 -> eip1559Transaction(payload, to);
|
||||
case ACCESS_LIST -> accessListTransaction(payload, to);
|
||||
case BLOB -> blobTransaction(payload, to);
|
||||
case SET_CODE -> null;
|
||||
case DELEGATE_CODE -> null;
|
||||
// no default, all types accounted for.
|
||||
};
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class TransactionTestFixture {
|
||||
builder.versionedHashes(versionedHashes.get());
|
||||
}
|
||||
break;
|
||||
case SET_CODE:
|
||||
case DELEGATE_CODE:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
package org.hyperledger.besu.ethereum.core.encoding;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
@@ -27,7 +26,7 @@ import java.math.BigInteger;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SetCodeTransactionDecoderTest {
|
||||
class CodeDelegationDecoderTest {
|
||||
|
||||
@Test
|
||||
void shouldDecodeInnerPayloadWithNonce() {
|
||||
@@ -36,14 +35,14 @@ class SetCodeTransactionDecoderTest {
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromHexString(
|
||||
"0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"),
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa562a80a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"),
|
||||
true);
|
||||
final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input);
|
||||
final CodeDelegation authorization = CodeDelegationTransactionDecoder.decodeInnerPayload(input);
|
||||
|
||||
assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE);
|
||||
assertThat(authorization.address())
|
||||
.isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"));
|
||||
assertThat(authorization.nonce().get()).isEqualTo(0L);
|
||||
assertThat(authorization.nonce()).isEqualTo(42);
|
||||
|
||||
final SECPSignature signature = authorization.signature();
|
||||
assertThat(signature.getRecId()).isEqualTo((byte) 0);
|
||||
@@ -54,20 +53,20 @@ class SetCodeTransactionDecoderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDecodeInnerPayloadWithoutNonce() {
|
||||
void shouldDecodeInnerPayloadWithNonceZero() {
|
||||
// "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5"
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromHexString(
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"),
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa568001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"),
|
||||
true);
|
||||
final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input);
|
||||
final CodeDelegation authorization = CodeDelegationTransactionDecoder.decodeInnerPayload(input);
|
||||
|
||||
assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE);
|
||||
assertThat(authorization.address())
|
||||
.isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"));
|
||||
assertThat(authorization.nonce()).isEmpty();
|
||||
assertThat(authorization.nonce()).isEqualTo(0);
|
||||
|
||||
final SECPSignature signature = authorization.signature();
|
||||
assertThat(signature.getRecId()).isEqualTo((byte) 1);
|
||||
@@ -78,37 +77,20 @@ class SetCodeTransactionDecoderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowInnerPayloadWithMultipleNonces() {
|
||||
// "d90194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c20107"
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromHexString(
|
||||
"0xf85c0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c2010201a0401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135aa0753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc"),
|
||||
true);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> {
|
||||
SetCodeTransactionDecoder.decodeInnerPayload(input);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDecodeInnerPayloadWithoutNonceAndChainIdZero() {
|
||||
void shouldDecodeInnerPayloadWithChainIdZero() {
|
||||
// "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5"
|
||||
|
||||
final BytesValueRLPInput input =
|
||||
new BytesValueRLPInput(
|
||||
Bytes.fromHexString(
|
||||
"0xf85a0094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"),
|
||||
"0xf85a8094633688abc3ccf8b0c03088d2d1c6ae4958c2fa560501a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"),
|
||||
true);
|
||||
final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input);
|
||||
final CodeDelegation authorization = CodeDelegationTransactionDecoder.decodeInnerPayload(input);
|
||||
|
||||
assertThat(authorization.chainId()).isEqualTo(BigInteger.ZERO);
|
||||
assertThat(authorization.address())
|
||||
.isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"));
|
||||
assertThat(authorization.nonce().isEmpty()).isTrue();
|
||||
assertThat(authorization.nonce()).isEqualTo(5);
|
||||
|
||||
final SECPSignature signature = authorization.signature();
|
||||
assertThat(signature.getRecId()).isEqualTo((byte) 1);
|
||||
@@ -19,11 +19,10 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.ethereum.core.CodeDelegation;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
@@ -31,7 +30,7 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class SetCodeTransactionEncoderTest {
|
||||
class CodeDelegationEncoderTest {
|
||||
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
|
||||
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
|
||||
|
||||
@@ -43,14 +42,14 @@ class SetCodeTransactionEncoderTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncodeSingleSetCodeWithNonce() {
|
||||
void shouldEncodeSingleCodeDelegationWithNonceAndChainId() {
|
||||
// "0xd80194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c105"
|
||||
|
||||
final SetCodeAuthorization authorization =
|
||||
new SetCodeAuthorization(
|
||||
final CodeDelegation authorization =
|
||||
new CodeDelegation(
|
||||
BigInteger.ONE,
|
||||
Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"),
|
||||
Optional.of(0L),
|
||||
42,
|
||||
SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.createSignature(
|
||||
@@ -60,23 +59,23 @@ class SetCodeTransactionEncoderTest {
|
||||
"3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16),
|
||||
(byte) 0));
|
||||
|
||||
SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output);
|
||||
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
|
||||
|
||||
assertThat(output.encoded())
|
||||
.isEqualTo(
|
||||
Bytes.fromHexString(
|
||||
"0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"));
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa562a80a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncodeSingleSetCodeWithoutNonce() {
|
||||
void shouldEncodeSingleCodeDelegationWithNonceZero() {
|
||||
// "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5"
|
||||
|
||||
final SetCodeAuthorization authorization =
|
||||
new SetCodeAuthorization(
|
||||
final CodeDelegation authorization =
|
||||
new CodeDelegation(
|
||||
BigInteger.ONE,
|
||||
Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"),
|
||||
Optional.empty(),
|
||||
0,
|
||||
SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.createSignature(
|
||||
@@ -86,23 +85,23 @@ class SetCodeTransactionEncoderTest {
|
||||
"25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16),
|
||||
(byte) 1));
|
||||
|
||||
SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output);
|
||||
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
|
||||
|
||||
assertThat(output.encoded())
|
||||
.isEqualTo(
|
||||
Bytes.fromHexString(
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"));
|
||||
"0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa568001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldEncodeSingleSetCodeWithoutNonceAndChainIdZero() {
|
||||
void shouldEncodeSingleCodeDelegationWithChainIdZero() {
|
||||
// "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5"
|
||||
|
||||
final SetCodeAuthorization authorization =
|
||||
new SetCodeAuthorization(
|
||||
final CodeDelegation authorization =
|
||||
new CodeDelegation(
|
||||
BigInteger.ZERO,
|
||||
Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"),
|
||||
Optional.empty(),
|
||||
5,
|
||||
SIGNATURE_ALGORITHM
|
||||
.get()
|
||||
.createSignature(
|
||||
@@ -112,11 +111,11 @@ class SetCodeTransactionEncoderTest {
|
||||
"3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16),
|
||||
(byte) 1));
|
||||
|
||||
SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output);
|
||||
CodeDelegationEncoder.encodeSingleCodeDelegation(authorization, output);
|
||||
|
||||
assertThat(output.encoded())
|
||||
.isEqualTo(
|
||||
Bytes.fromHexString(
|
||||
"0xf85a8094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"));
|
||||
"0xf85a8094633688abc3ccf8b0c03088d2d1c6ae4958c2fa560501a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"));
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ class MainnetTransactionProcessorTest {
|
||||
MAX_STACK_SIZE,
|
||||
FeeMarket.legacy(),
|
||||
CoinbaseFeePriceCalculator.frontier(),
|
||||
new AuthorityProcessor(Optional.of(BigInteger.ONE)));
|
||||
new CodeDelegationProcessor(Optional.of(BigInteger.ONE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -147,7 +147,7 @@ public abstract class PendingTransaction
|
||||
case ACCESS_LIST -> computeAccessListMemorySize();
|
||||
case EIP1559 -> computeEIP1559MemorySize();
|
||||
case BLOB -> computeBlobMemorySize();
|
||||
case SET_CODE -> computeSetCodeMemorySize();
|
||||
case DELEGATE_CODE -> computeSetCodeMemorySize();
|
||||
}
|
||||
+ PENDING_TRANSACTION_MEMORY_SIZE;
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class BaseTransactionPoolTest {
|
||||
final TransactionType txType = TransactionType.values()[randomizeTxType.nextInt(4)];
|
||||
|
||||
return switch (txType) {
|
||||
case FRONTIER, ACCESS_LIST, EIP1559, SET_CODE ->
|
||||
case FRONTIER, ACCESS_LIST, EIP1559, DELEGATE_CODE ->
|
||||
createTransaction(txType, nonce, maxGasPrice, payloadSize, keys);
|
||||
case BLOB ->
|
||||
createTransaction(
|
||||
|
||||
@@ -1598,7 +1598,7 @@ public class LayersTest extends BaseTransactionPoolTest {
|
||||
case ACCESS_LIST -> createAccessListPendingTransaction(sender, nonce);
|
||||
case EIP1559 -> createEIP1559PendingTransaction(sender, nonce);
|
||||
case BLOB -> createBlobPendingTransaction(sender, nonce);
|
||||
case SET_CODE -> throw new UnsupportedOperationException();
|
||||
case DELEGATE_CODE -> throw new UnsupportedOperationException();
|
||||
};
|
||||
liveTxsBySender.get(sender).put(nonce, newPendingTx);
|
||||
return newPendingTx;
|
||||
|
||||
@@ -22,10 +22,12 @@ import static org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocol
|
||||
|
||||
import org.hyperledger.besu.config.StubGenesisConfigOptions;
|
||||
import org.hyperledger.besu.crypto.KeyPair;
|
||||
import org.hyperledger.besu.crypto.SECPSignature;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithm;
|
||||
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
|
||||
import org.hyperledger.besu.datatypes.AccessListEntry;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.TransactionType;
|
||||
import org.hyperledger.besu.datatypes.VersionedHash;
|
||||
@@ -35,7 +37,6 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.ConsolidationRequest;
|
||||
import org.hyperledger.besu.ethereum.core.DepositRequest;
|
||||
import org.hyperledger.besu.ethereum.core.Request;
|
||||
import org.hyperledger.besu.ethereum.core.SetCodeAuthorization;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.core.WithdrawalRequest;
|
||||
@@ -223,8 +224,7 @@ public class T8nExecutor {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<org.hyperledger.besu.datatypes.SetCodeAuthorization> authorizations =
|
||||
new ArrayList<>(authorizationList.size());
|
||||
List<CodeDelegation> authorizations = new ArrayList<>(authorizationList.size());
|
||||
for (JsonNode entryAsJson : authorizationList) {
|
||||
final BigInteger authorizationChainId =
|
||||
Bytes.fromHexStringLenient(entryAsJson.get("chainId").textValue())
|
||||
@@ -232,26 +232,8 @@ public class T8nExecutor {
|
||||
final Address authorizationAddress =
|
||||
Address.fromHexString(entryAsJson.get("address").textValue());
|
||||
|
||||
JsonNode nonces = entryAsJson.get("nonce");
|
||||
|
||||
if (nonces == null) {
|
||||
out.printf(
|
||||
"TX json node unparseable: expected nonce field to be provided - %s%n",
|
||||
txNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Long> authorizationNonces;
|
||||
if (nonces.isArray()) {
|
||||
authorizationNonces = new ArrayList<>(nonces.size());
|
||||
for (JsonNode nonceAsJson : nonces) {
|
||||
authorizationNonces.add(
|
||||
Bytes.fromHexStringLenient(nonceAsJson.textValue()).toLong());
|
||||
}
|
||||
} else {
|
||||
authorizationNonces =
|
||||
List.of(Bytes.fromHexStringLenient(nonces.textValue()).toLong());
|
||||
}
|
||||
final long authorizationNonce =
|
||||
Bytes.fromHexStringLenient(entryAsJson.get("nonce").textValue()).toLong();
|
||||
|
||||
final byte authorizationV =
|
||||
Bytes.fromHexStringLenient(entryAsJson.get("v").textValue())
|
||||
@@ -264,16 +246,17 @@ public class T8nExecutor {
|
||||
Bytes.fromHexStringLenient(entryAsJson.get("s").textValue())
|
||||
.toUnsignedBigInteger();
|
||||
|
||||
final SECPSignature authorizationSignature =
|
||||
new SECPSignature(authorizationR, authorizationS, authorizationV);
|
||||
|
||||
authorizations.add(
|
||||
SetCodeAuthorization.createSetCodeAuthorizationEntry(
|
||||
new org.hyperledger.besu.ethereum.core.CodeDelegation(
|
||||
authorizationChainId,
|
||||
authorizationAddress,
|
||||
authorizationNonces,
|
||||
authorizationV,
|
||||
authorizationR,
|
||||
authorizationS));
|
||||
authorizationNonce,
|
||||
authorizationSignature));
|
||||
}
|
||||
builder.setCodeTransactionPayloads(authorizations);
|
||||
builder.codeDelegations(authorizations);
|
||||
}
|
||||
|
||||
if (txNode.has("blobVersionedHashes")) {
|
||||
@@ -328,8 +311,8 @@ public class T8nExecutor {
|
||||
} else {
|
||||
out.printf("TX json node unparseable: %s%n", txNode);
|
||||
}
|
||||
} catch (IllegalArgumentException iae) {
|
||||
rejections.add(new RejectedTransaction(i, iae.getMessage()));
|
||||
} catch (IllegalArgumentException | ArithmeticException e) {
|
||||
rejections.add(new RejectedTransaction(i, e.getMessage()));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ package org.hyperledger.besu.evm.account;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* A world state account.
|
||||
*
|
||||
@@ -49,4 +53,31 @@ public interface Account extends AccountState {
|
||||
* is set.
|
||||
*/
|
||||
boolean isStorageEmpty();
|
||||
|
||||
/**
|
||||
* Returns the address of the delegated code account if it has one.
|
||||
*
|
||||
* @return the address of the delegated code account if it has one otherwise empty.
|
||||
*/
|
||||
default Optional<Address> delegatedCodeAddress() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean to indicate if the account has delegated code.
|
||||
*
|
||||
* @return true if the account has delegated code otherwise false.
|
||||
*/
|
||||
default boolean hasDelegatedCode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code as it is stored in the trie even if it's a delegated code account.
|
||||
*
|
||||
* @return the code as it is stored in the trie.
|
||||
*/
|
||||
default Bytes getUnprocessedCode() {
|
||||
return getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.evm.account;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
class BaseDelegatedCodeAccount {
|
||||
private final WorldUpdater worldUpdater;
|
||||
|
||||
/** 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) {
|
||||
this.worldUpdater = worldUpdater;
|
||||
this.delegatedCodeAddress = delegatedCodeAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delegated code.
|
||||
*
|
||||
* @return the delegated code.
|
||||
*/
|
||||
protected Bytes getCode() {
|
||||
return resolveDelegatedCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash of the delegated code.
|
||||
*
|
||||
* @return the hash of the delegated code.
|
||||
*/
|
||||
protected Hash getCodeHash() {
|
||||
final Bytes code = getCode();
|
||||
return (code == null || code.isEmpty()) ? Hash.EMPTY : Hash.hash(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the balance of the delegated account.
|
||||
*
|
||||
* @return the balance of the delegated account.
|
||||
*/
|
||||
protected Wei getDelegatedBalance() {
|
||||
return getDelegatedAccount().map(Account::getBalance).orElse(Wei.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce of the delegated account.
|
||||
*
|
||||
* @return the nonce of the delegated account.
|
||||
*/
|
||||
protected long getDelegatedNonce() {
|
||||
return getDelegatedAccount().map(Account::getNonce).orElse(Account.DEFAULT_NONCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the delegated code.
|
||||
*
|
||||
* @return the address of the delegated code.
|
||||
*/
|
||||
protected Optional<Address> delegatedCodeAddress() {
|
||||
return Optional.of(delegatedCodeAddress);
|
||||
}
|
||||
|
||||
private Optional<Account> getDelegatedAccount() {
|
||||
return Optional.ofNullable(worldUpdater.getAccount(delegatedCodeAddress));
|
||||
}
|
||||
|
||||
private Bytes resolveDelegatedCode() {
|
||||
|
||||
return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(Bytes.EMPTY);
|
||||
}
|
||||
}
|
||||
@@ -17,30 +17,33 @@ package org.hyperledger.besu.evm.account;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** Wraps an EOA account and includes authorized code to be run on behalf of it. */
|
||||
public class AuthorizedCodeAccount implements Account {
|
||||
private final Account wrappedAccount;
|
||||
private final Bytes authorizedCode;
|
||||
/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
|
||||
public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Account {
|
||||
|
||||
/** The hash of the authorized code. */
|
||||
protected Hash codeHash = null;
|
||||
private final Account wrappedAccount;
|
||||
|
||||
/**
|
||||
* Creates a new AuthorizedCodeAccount.
|
||||
*
|
||||
* @param wrappedAccount the account that has authorized code to be loaded into it.
|
||||
* @param authorizedCode the authorized code.
|
||||
* @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.
|
||||
*/
|
||||
public AuthorizedCodeAccount(final Account wrappedAccount, final Bytes authorizedCode) {
|
||||
public DelegatedCodeAccount(
|
||||
final WorldUpdater worldUpdater,
|
||||
final Account wrappedAccount,
|
||||
final Address codeDelegationAddress) {
|
||||
super(worldUpdater, codeDelegationAddress);
|
||||
this.wrappedAccount = wrappedAccount;
|
||||
this.authorizedCode = authorizedCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,6 +56,11 @@ public class AuthorizedCodeAccount implements Account {
|
||||
return wrappedAccount.isStorageEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Address> delegatedCodeAddress() {
|
||||
return super.delegatedCodeAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getAddressHash() {
|
||||
return wrappedAccount.getAddressHash();
|
||||
@@ -70,16 +78,17 @@ public class AuthorizedCodeAccount implements Account {
|
||||
|
||||
@Override
|
||||
public Bytes getCode() {
|
||||
return authorizedCode;
|
||||
return super.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getUnprocessedCode() {
|
||||
return wrappedAccount.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getCodeHash() {
|
||||
if (codeHash == null) {
|
||||
codeHash = authorizedCode.equals(Bytes.EMPTY) ? Hash.EMPTY : Hash.hash(authorizedCode);
|
||||
}
|
||||
|
||||
return codeHash;
|
||||
return super.getCodeHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,9 +101,24 @@ public class AuthorizedCodeAccount implements Account {
|
||||
return wrappedAccount.getOriginalStorageValue(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCode() {
|
||||
return !getCode().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<Bytes32, AccountStorageEntry> storageEntriesFrom(
|
||||
final Bytes32 startKeyHash, final int limit) {
|
||||
return wrappedAccount.storageEntriesFrom(startKeyHash, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -17,33 +17,35 @@ package org.hyperledger.besu.evm.account;
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** Wraps a mutable EOA account and includes authorized code to be run on behalf of it. */
|
||||
public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
|
||||
public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
implements MutableAccount {
|
||||
|
||||
private final MutableAccount wrappedAccount;
|
||||
private final Bytes authorizedCode;
|
||||
|
||||
/** The hash of the authorized code. */
|
||||
protected Hash codeHash = null;
|
||||
|
||||
/**
|
||||
* Creates a new MutableAuthorizedCodeAccount.
|
||||
*
|
||||
* @param wrappedAccount the account that has authorized code to be loaded into it.
|
||||
* @param authorizedCode the authorized code.
|
||||
* @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.
|
||||
*/
|
||||
public MutableAuthorizedCodeAccount(
|
||||
final MutableAccount wrappedAccount, final Bytes authorizedCode) {
|
||||
public MutableDelegatedCodeAccount(
|
||||
final WorldUpdater worldUpdater,
|
||||
final MutableAccount wrappedAccount,
|
||||
final Address codeDelegationAddress) {
|
||||
super(worldUpdater, codeDelegationAddress);
|
||||
this.wrappedAccount = wrappedAccount;
|
||||
this.authorizedCode = authorizedCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,6 +58,11 @@ public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
return wrappedAccount.isStorageEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Address> delegatedCodeAddress() {
|
||||
return super.delegatedCodeAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getAddressHash() {
|
||||
return wrappedAccount.getAddressHash();
|
||||
@@ -73,16 +80,17 @@ public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
|
||||
@Override
|
||||
public Bytes getCode() {
|
||||
return authorizedCode;
|
||||
return super.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getUnprocessedCode() {
|
||||
return wrappedAccount.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getCodeHash() {
|
||||
if (codeHash == null) {
|
||||
codeHash = authorizedCode.equals(Bytes.EMPTY) ? Hash.EMPTY : Hash.hash(authorizedCode);
|
||||
}
|
||||
|
||||
return codeHash;
|
||||
return super.getCodeHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,6 +103,16 @@ public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
return wrappedAccount.getOriginalStorageValue(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCode() {
|
||||
return !getCode().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<Bytes32, AccountStorageEntry> storageEntriesFrom(
|
||||
final Bytes32 startKeyHash, final int limit) {
|
||||
@@ -113,7 +131,7 @@ public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
|
||||
@Override
|
||||
public void setCode(final Bytes code) {
|
||||
throw new RuntimeException("Cannot set code on an AuthorizedCodeAccount");
|
||||
wrappedAccount.setCode(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,4 +153,9 @@ public class MutableAuthorizedCodeAccount implements MutableAccount {
|
||||
public void becomeImmutable() {
|
||||
wrappedAccount.becomeImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -647,12 +647,33 @@ public interface GasCalculator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the upfront gas cost for EIP 7702 operation.
|
||||
* Returns the upfront gas cost for EIP 7702 authorization processing.
|
||||
*
|
||||
* @param authorizationListLength The length of the authorization list
|
||||
* @param delegateCodeListLength The length of the code delegation list
|
||||
* @return the gas cost
|
||||
*/
|
||||
default long setCodeListGasCost(final int authorizationListLength) {
|
||||
default long delegateCodeGasCost(final int delegateCodeListLength) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the refund for proessing the 7702 code delegation list if an delegater account
|
||||
* already exist in the trie.
|
||||
*
|
||||
* @param alreadyExistingAccountSize The number of accounts already in the trie
|
||||
* @return the gas refund
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,19 +16,17 @@ package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
|
||||
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
|
||||
/**
|
||||
* Gas Calculator for Prague
|
||||
*
|
||||
* <p>Placeholder for new gas schedule items. If Prague finalzies without changes this can be
|
||||
* removed
|
||||
*
|
||||
* <UL>
|
||||
* <LI>TBD
|
||||
* <LI>Gas costs for EIP-7702 (Code Delegation)
|
||||
* </UL>
|
||||
*/
|
||||
public class PragueGasCalculator extends CancunGasCalculator {
|
||||
|
||||
static final long PER_CONTRACT_CODE_BASE_COST = 2500L;
|
||||
final long existingAccountGasRefund;
|
||||
|
||||
/** Instantiates a new Prague Gas Calculator. */
|
||||
public PragueGasCalculator() {
|
||||
@@ -42,10 +40,21 @@ public class PragueGasCalculator extends CancunGasCalculator {
|
||||
*/
|
||||
protected PragueGasCalculator(final int maxPrecompile) {
|
||||
super(maxPrecompile);
|
||||
this.existingAccountGasRefund = newAccountGasCost() - CodeDelegation.PER_AUTH_BASE_COST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long setCodeListGasCost(final int authorizationListLength) {
|
||||
return PER_CONTRACT_CODE_BASE_COST * authorizationListLength;
|
||||
public long delegateCodeGasCost(final int delegateCodeListLength) {
|
||||
return newAccountGasCost() * delegateCodeListLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) {
|
||||
return existingAccountGasRefund * alreadyExistingAccounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long delegatedCodeResolutionGasCost(final boolean isWarm) {
|
||||
return isWarm ? getWarmStorageReadCost() : getColdAccountAccessCost();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -26,6 +27,7 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame.State;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -190,6 +192,15 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress());
|
||||
final Wei balance = account == null ? Wei.ZERO : account.getBalance();
|
||||
// If the call is sending more value than the account has or the message frame is to deep
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
@@ -24,6 +26,7 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@@ -119,6 +122,16 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
}
|
||||
Address to = Words.toAddress(toBytes);
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
boolean accountCreation = contract == null && !zeroValue;
|
||||
long cost =
|
||||
gasCalculator().memoryExpansionGasCost(frame, inputOffset, inputLength)
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
@@ -25,6 +26,7 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -93,6 +95,16 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
}
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
|
||||
|
||||
if (enableEIP3540
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
@@ -25,6 +27,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -78,23 +81,34 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
final long cost = cost(accountIsWarm);
|
||||
if (frame.getRemainingGas() < cost) {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
} else {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
if (account == null || account.isEmpty()) {
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
&& code.get(1) == 0) {
|
||||
frame.pushStackItem(EOF_REPLACEMENT_HASH);
|
||||
} else {
|
||||
frame.pushStackItem(account.getCodeHash());
|
||||
}
|
||||
}
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
if (account == null || account.isEmpty()) {
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
&& code.get(1) == 0) {
|
||||
frame.pushStackItem(EOF_REPLACEMENT_HASH);
|
||||
} else {
|
||||
frame.pushStackItem(account.getCodeHash());
|
||||
}
|
||||
}
|
||||
return new OperationResult(cost, null);
|
||||
|
||||
} catch (final UnderflowException ufe) {
|
||||
return new OperationResult(cost(true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
|
||||
} catch (final OverflowException ofe) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
@@ -24,6 +26,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -78,6 +81,16 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
} else {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
Bytes codeSize;
|
||||
if (account == null) {
|
||||
codeSize = Bytes.EMPTY;
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.evm.worldstate;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.AuthorizedCodeAccount;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.account.MutableAuthorizedCodeAccount;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** A service that manages the code injection of authorized code. */
|
||||
public class AuthorizedCodeService {
|
||||
private final Map<Address, Bytes> authorizedCode = new HashMap<>();
|
||||
|
||||
/** Creates a new AuthorizedCodeService. */
|
||||
public AuthorizedCodeService() {}
|
||||
|
||||
/**
|
||||
* Authorizes to load the code of authorizedCode into the authorizer account.
|
||||
*
|
||||
* @param authorizer the address that gives the authorization.
|
||||
* @param authorizedCode the code which will be loaded.
|
||||
*/
|
||||
public void addAuthorizedCode(final Address authorizer, final Bytes authorizedCode) {
|
||||
this.authorizedCode.put(authorizer, authorizedCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all the authorities that have given their authorization to load the code of another
|
||||
* account.
|
||||
*
|
||||
* @return the set of authorities.
|
||||
*/
|
||||
public Set<Address> getAuthorities() {
|
||||
return authorizedCode.keySet();
|
||||
}
|
||||
|
||||
/** Resets all the authorized accounts. */
|
||||
public void resetAuthorities() {
|
||||
authorizedCode.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the provided address has set an authorized to load code into an EOA account.
|
||||
*
|
||||
* @param authority the address to check.
|
||||
* @return {@code true} if the address has been authorized, {@code false} otherwise.
|
||||
*/
|
||||
public boolean hasAuthorizedCode(final Address authority) {
|
||||
return authorizedCode.containsKey(authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the provided account, injecting the authorized code if authorized.
|
||||
*
|
||||
* @param worldUpdater the world updater to retrieve the code account.
|
||||
* @param originalAccount the account to process.
|
||||
* @param address the address of the account in case the provided account is null
|
||||
* @return the processed account, containing the authorized code if authorized.
|
||||
*/
|
||||
public Account processAccount(
|
||||
final WorldUpdater worldUpdater, final Account originalAccount, final Address address) {
|
||||
if (!authorizedCode.containsKey(address)) {
|
||||
return originalAccount;
|
||||
}
|
||||
|
||||
Account account = originalAccount;
|
||||
if (account == null) {
|
||||
account = worldUpdater.createAccount(address);
|
||||
}
|
||||
|
||||
return new AuthorizedCodeAccount(account, authorizedCode.get(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the provided mutable account, injecting the authorized code if authorized.
|
||||
*
|
||||
* @param worldUpdater the world updater to retrieve the code account.
|
||||
* @param originalAccount the mutable account to process.
|
||||
* @param address the address of the account in case the provided account is null
|
||||
* @return the processed mutable account, containing the authorized code if authorized.
|
||||
*/
|
||||
public MutableAccount processMutableAccount(
|
||||
final WorldUpdater worldUpdater,
|
||||
final MutableAccount originalAccount,
|
||||
final Address address) {
|
||||
if (!authorizedCode.containsKey(address)) {
|
||||
return originalAccount;
|
||||
}
|
||||
|
||||
MutableAccount account = originalAccount;
|
||||
if (account == null) {
|
||||
account = worldUpdater.createAccount(address);
|
||||
}
|
||||
|
||||
return new MutableAuthorizedCodeAccount(account, authorizedCode.get(address));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.evm.worldstate;
|
||||
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
|
||||
/**
|
||||
* Helper class to deduct gas cost for delegated code resolution.
|
||||
*
|
||||
* <p>Delegated code resolution is the process of determining the address of the contract that will
|
||||
* be executed when a contract has delegated code. This process is necessary to determine the
|
||||
* contract that will be executed and to ensure that the contract is warm in the cache.
|
||||
*/
|
||||
public class DelegatedCodeGasCostHelper {
|
||||
|
||||
/** Private constructor to prevent instantiation. */
|
||||
private DelegatedCodeGasCostHelper() {
|
||||
// empty constructor
|
||||
}
|
||||
|
||||
/** The status of the operation. */
|
||||
public enum Status {
|
||||
/** The operation failed due to insufficient gas. */
|
||||
INSUFFICIENT_GAS,
|
||||
/** The operation was successful. */
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of the operation.
|
||||
*
|
||||
* @param gasCost the gas cost
|
||||
* @param status of the operation
|
||||
*/
|
||||
public record Result(long gasCost, Status status) {}
|
||||
|
||||
/**
|
||||
* Deducts the gas cost for delegated code resolution.
|
||||
*
|
||||
* @param frame the message frame
|
||||
* @param gasCalculator the gas calculator
|
||||
* @param account the account
|
||||
* @return the gas cost and result of the operation
|
||||
*/
|
||||
public static Result deductDelegatedCodeGasCost(
|
||||
final MessageFrame frame, final GasCalculator gasCalculator, final Account account) {
|
||||
if (!account.hasDelegatedCode()) {
|
||||
return new Result(0, Status.SUCCESS);
|
||||
}
|
||||
|
||||
if (account.delegatedCodeAddress().isEmpty()) {
|
||||
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);
|
||||
|
||||
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
|
||||
return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
return new Result(delegatedCodeResolutionGas, Status.SUCCESS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright contributors to Hyperledger Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.evm.worldstate;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.DelegatedCodeAccount;
|
||||
import org.hyperledger.besu.evm.account.MutableAccount;
|
||||
import org.hyperledger.besu.evm.account.MutableDelegatedCodeAccount;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** A service that manages the code injection of delegated code. */
|
||||
public class DelegatedCodeService {
|
||||
private static final Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
|
||||
private static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
|
||||
|
||||
/** Creates a new DelegatedCodeService. */
|
||||
public DelegatedCodeService() {}
|
||||
|
||||
/**
|
||||
* Add the delegated code to the given account.
|
||||
*
|
||||
* @param account the account to which the delegated code is added.
|
||||
* @param delegatedCodeAddress the address of the delegated code.
|
||||
*/
|
||||
public void addDelegatedCode(final MutableAccount account, final Address delegatedCodeAddress) {
|
||||
account.setCode(Bytes.concatenate(DELEGATED_CODE_PREFIX, delegatedCodeAddress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the provided account has either no code set or has already delegated code.
|
||||
*
|
||||
* @param account the account to check.
|
||||
* @return {@code true} if the account can set delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public boolean canSetDelegatedCode(final Account account) {
|
||||
return account.getCode().isEmpty() || hasDelegatedCode(account.getUnprocessedCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the provided account, resolving the code if delegated.
|
||||
*
|
||||
* @param worldUpdater the world updater to retrieve the delegated code.
|
||||
* @param account the account to process.
|
||||
* @return the processed account, containing the delegated code if set, the unmodified account
|
||||
* otherwise.
|
||||
*/
|
||||
public Account processAccount(final WorldUpdater worldUpdater, final Account account) {
|
||||
if (account == null || !hasDelegatedCode(account.getCode())) {
|
||||
return account;
|
||||
}
|
||||
|
||||
return new DelegatedCodeAccount(
|
||||
worldUpdater, account, resolveDelegatedAddress(account.getCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the provided mutable account, resolving the code if delegated.
|
||||
*
|
||||
* @param worldUpdater the world updater to retrieve the delegated code.
|
||||
* @param account the mutable account to process.
|
||||
* @return the processed mutable account, containing the delegated code if set, the unmodified
|
||||
* mutable account otherwise.
|
||||
*/
|
||||
public MutableAccount processMutableAccount(
|
||||
final WorldUpdater worldUpdater, final MutableAccount account) {
|
||||
if (account == null || !hasDelegatedCode(account.getCode())) {
|
||||
return account;
|
||||
}
|
||||
|
||||
return new MutableDelegatedCodeAccount(
|
||||
worldUpdater, account, resolveDelegatedAddress(account.getCode()));
|
||||
}
|
||||
|
||||
private Address resolveDelegatedAddress(final Bytes code) {
|
||||
return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size()));
|
||||
}
|
||||
|
||||
private boolean hasDelegatedCode(final Bytes code) {
|
||||
return code != null
|
||||
&& code.size() == DELEGATED_CODE_SIZE
|
||||
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ import java.util.Optional;
|
||||
*/
|
||||
public class EVMWorldUpdater implements WorldUpdater {
|
||||
private final WorldUpdater rootWorldUpdater;
|
||||
private final AuthorizedCodeService authorizedCodeService;
|
||||
private final DelegatedCodeService delegatedCodeService;
|
||||
|
||||
/**
|
||||
* Instantiates a new EVM world updater.
|
||||
@@ -37,13 +37,13 @@ public class EVMWorldUpdater implements WorldUpdater {
|
||||
* @param rootWorldUpdater the root world updater
|
||||
*/
|
||||
public EVMWorldUpdater(final WorldUpdater rootWorldUpdater) {
|
||||
this(rootWorldUpdater, new AuthorizedCodeService());
|
||||
this(rootWorldUpdater, new DelegatedCodeService());
|
||||
}
|
||||
|
||||
private EVMWorldUpdater(
|
||||
final WorldUpdater rootWorldUpdater, final AuthorizedCodeService authorizedCodeService) {
|
||||
final WorldUpdater rootWorldUpdater, final DelegatedCodeService delegatedCodeService) {
|
||||
this.rootWorldUpdater = rootWorldUpdater;
|
||||
this.authorizedCodeService = authorizedCodeService;
|
||||
this.delegatedCodeService = delegatedCodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,38 +51,36 @@ public class EVMWorldUpdater implements WorldUpdater {
|
||||
*
|
||||
* @return the authorized code service
|
||||
*/
|
||||
public AuthorizedCodeService authorizedCodeService() {
|
||||
return authorizedCodeService;
|
||||
public DelegatedCodeService authorizedCodeService() {
|
||||
return delegatedCodeService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAccount createAccount(final Address address, final long nonce, final Wei balance) {
|
||||
return authorizedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.createAccount(address, nonce, balance), address);
|
||||
return delegatedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.createAccount(address, nonce, balance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAccount getAccount(final Address address) {
|
||||
return authorizedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getAccount(address), address);
|
||||
return delegatedCodeService.processMutableAccount(this, rootWorldUpdater.getAccount(address));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAccount getOrCreate(final Address address) {
|
||||
return authorizedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getOrCreate(address), address);
|
||||
return delegatedCodeService.processMutableAccount(this, rootWorldUpdater.getOrCreate(address));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAccount getOrCreateSenderAccount(final Address address) {
|
||||
return authorizedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getOrCreateSenderAccount(address), address);
|
||||
return delegatedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getOrCreateSenderAccount(address));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAccount getSenderAccount(final MessageFrame frame) {
|
||||
return authorizedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getSenderAccount(frame), frame.getSenderAddress());
|
||||
return delegatedCodeService.processMutableAccount(
|
||||
this, rootWorldUpdater.getSenderAccount(frame));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,11 +115,11 @@ public class EVMWorldUpdater implements WorldUpdater {
|
||||
|
||||
@Override
|
||||
public WorldUpdater updater() {
|
||||
return new EVMWorldUpdater(rootWorldUpdater.updater(), authorizedCodeService);
|
||||
return new EVMWorldUpdater(rootWorldUpdater.updater(), delegatedCodeService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Account get(final Address address) {
|
||||
return authorizedCodeService.processAccount(this, rootWorldUpdater.get(address), address);
|
||||
return delegatedCodeService.processAccount(this, rootWorldUpdater.get(address));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user