mirror of https://github.com/hyperledger/besu
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>pull/7586/head
parent
cf592c48d1
commit
8eee569887
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
@ -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); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue