From 69054738e56852abcf5e96bea7efc0c6e02a8b97 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Mon, 13 Jan 2025 05:53:02 +0800 Subject: [PATCH] multiple fixes and refinements --- EIPS/eip-7851.md | 70 +++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 922f4b4ff265ec..11e4ac1deb352e 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -3,7 +3,7 @@ eip: 7851 title: Deactivate/Reactivate a Delegated EOA's Key description: Introduce a new precompiled contract for EOAs with delegated code to deactivate or reactivate private keys. author: Liyi Guo (@colinlyguo) -discussions-to: https://ethereum-magicians.org/t/eip-7851-eoa-private-key-deactivation-reactivation/22344 +discussions-to: https://ethereum-magicians.org/t/eip-7851-deactivate-reactivate-a-delegated-eoas-key/22344 status: Draft type: Standards Track category: Core @@ -30,7 +30,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S | Constant | Value | |-----------------------------------|----------------------| | `PRECOMPILE_ADDRESS` | `0xTBD` | -| `PRECOMPILE_GAS_COST` | `5000` (tentative) | +| `PRECOMPILE_GAS_COST` | `5000` | ### Delegated code encoding @@ -60,7 +60,7 @@ For EOAs with delegated code (begins with the prefix `0xef0100`), the following - The transaction pool MUST implement the same validation to prevent invalid transactions from propagating across the network. - Any [EIP-7702](./eip-7702) authorization from an authority with a deactivated delegated code MUST be considered invalid and skipped. The gas consumption rules remain unchanged, consistent with [EIP-7702](./eip-7702). -### Gas Cost +### Gas cost The `PER_EMPTY_ACCOUNT_COST` and `PER_AUTH_BASE_COST` constants defined in [EIP-7702](./eip-7702) remain unchanged, since account code will be loaded during the authorization validation. This EIP only adds a code length check, which is a small overhead compared to existing logic. @@ -68,10 +68,10 @@ The `PER_EMPTY_ACCOUNT_COST` and `PER_AUTH_BASE_COST` constants defined in [EIP- ### Using a precompiled contract -Alternative methods for implementing this feature include: +Alternative methods for deactivating and reactivating EOA private keys include: - Adding a new transaction type: Introducing a new transaction type could provide a mechanism to deactivate and reactivate EOA private keys. However, reactivating the private key would rely on a delegated contract as the authorizer, which complicates defining the rules for the new transaction type. -- Deploying a regular smart contract: A regular deployed contract could track the deactivated status of each address. However, invoking this contract, which executes bytecode and accesses storage, during deactivation and reactivation, would be more expensive than using a precompiled contract. Additionally, this approach "leaks" the design of a (widely used) programming language into the Ethereum core protocol. While it poses no obvious security risks, it is not ideal from a design perspective. +- Deploying a smart contract: Using a smart contract to track the deactivated status of each address. However, calling this contract, which executes bytecode, is more expensive than a precompiled contract. ### In-protocol reactivation @@ -83,29 +83,21 @@ The reactivation process is recommended to include a signed authorization from t Users should delegate their EOAs only to wallets that have been thoroughly audited and follow best practices for security. -### `5000` Gas `PRECOMPILE_GAS_COST` +### `5000` gas `PRECOMPILE_GAS_COST` -The `5000` gas cost is sufficient to cover validation, computation, and storage updates for the delegated code. +The `5000` gas cost is sufficient for the precompiled contract's validation logic and storage updates. ### Alternative EOA deprecation approach -One alternative deprecation approach involves using a hard fork to edit all existing and new EOAs to upgradeable smart contracts, which utilize the original EOA private key for authorization. Users can then upgrade these smart contracts to achieve more granular permission control. However, this approach is incompatible with EOAs already delegated to smart contracts, as it will overwrite the existing smart contract implementations. The EIP aims to fill this migration gap. +One alternative deprecation approach involves using a hard fork to edit all existing and new EOAs to pre-written upgradeable smart contracts, which utilize the original EOA private key for authorization. Users can add and replace keys, or upgrade the smart contracts to other implementations. However, this approach is incompatible with EOAs already delegated to smart contracts, as it will overwrite the existing smart contract implementations. This EIP aims to fill this migration gap. ### Avoiding delegated code prefix modification -This EIP appends a byte (`0x00`) to the delegated code instead of modifying the prefix (`0xef0100`) of [EIP-7702](./eip-7702) to ensure forward compatibility. If new prefixes such as `0xef0101` are introduced in the future, changing the prefix (e.g. to `0xef01ff`) makes it unclear which prefix to restore upon reactivation. +This EIP appends a byte (`0x00`) to the delegated code instead of modifying the prefix (`0xef0100`) of [EIP-7702](./eip-7702) to ensure forward compatibility. If new prefixes such as `0xef0101` are introduced in the future, changing the prefix to represent the deactivated status (e.g. `0xef01ff`) makes it unclear which prefix to restore (`0xef0100` or `0xef0101`) upon reactivation. ### Avoiding account state changes -Another alternative is to add a bool field `deactivated` in the account state to track the status. However, this approach will introduce backward compatibility logic and more test vectors related to this optional field when enabling this EIP, because the field is not present in existing accounts. - -### Forwards compatibility for removing EOAs - -After all existing and future EOAs have been migrated to smart contracts. It's natural and also easy to deprecate this EIP with a single upgrade, which involves some clean-ups: - -- Removing the precompiled contract. -- Removing all validation logic of the deactivation status since all EOAs are smart contracts. -- Removing the appended `0x00` byte from the delegated code of deactivated EOAs, which this EIP introduces. +An alternative is to add a `deactivated` field in the account state to track the status. However, this approach will introduce backward compatibility logic and more test vectors related to this optional field when enabling this EIP, because the field is not present in existing accounts. ## Backwards Compatibility @@ -121,25 +113,25 @@ precompile = PrecompiledContract() # Test 1: Valid activation and deactivation caller = "0x0123" delegated_addr = bytes.fromhex("1122334455667788990011223344556677889900") -active_code = PrecompiledContract.DELEGATED_CODE_PREFIX + delegated_addr +active_code = PrecompiledContract.DELEGATED_CODE_PREFIX + delegated_addr # Active state state_db.set_code(caller, active_code) error, gas_left = precompile.execute(caller, state_db, gas=10000) assert error == b"" -assert state_db.get_code(caller) == active_code + b"\x00" # Deactivated -assert gas_left == 10000 - PrecompiledContract.GAS_COST +assert state_db.get_code(caller) == active_code + b"\x00" # Deactivated state +assert gas_left == 10000 - PrecompiledContract.PRECOMPILE_GAS_COST error, gas_left = precompile.execute(caller, state_db, gas=10000) assert error == b"" -assert state_db.get_code(caller) == active_code # Activated -assert gas_left == 10000 - PrecompiledContract.GAS_COST +assert state_db.get_code(caller) == active_code # Reactivated state +assert gas_left == 10000 - PrecompiledContract.PRECOMPILE_GAS_COST # Test 2: Error cases -error, gas_left = precompile.execute(caller, state_db, gas=10000, static=True) -assert error == b"cannot call in static context" +error, gas_left = precompile.execute(caller, state_db, gas=10000, read_only=True) +assert error == b"STATICCALL disallows state modification" assert gas_left == 0 -error, gas_left = precompile.execute(caller, state_db, gas=PrecompiledContract.GAS_COST-1) +error, gas_left = precompile.execute(caller, state_db, gas=PrecompiledContract.PRECOMPILE_GAS_COST-1) assert error == b"insufficient gas" assert gas_left == 0 @@ -161,18 +153,18 @@ assert gas_left == 0 ```python class PrecompiledContract: - DELEGATED_CODE_PREFIX = bytes.fromhex("ef0100") # EIP-7702 prefix - GAS_COST = 5000 # PRECOMPILE_GAS_COST + DELEGATED_CODE_PREFIX = bytes.fromhex("ef0100") + PRECOMPILE_GAS_COST = 5000 - def execute(self, caller, state_db, gas, static=False): + def execute(self, caller, state_db, gas, read_only=False): """ - Toggle EOA's private key authorization between active/deactivated states. + Switch the private key state of delegated EOAs between active and deactivated. Parameters: - caller: The address calling the contract - state_db: The state database - gas: Gas provided for execution - - static: Whether called in read-only context + - read_only: Whether called in read-only context Returns: - Tuple of (result, gas_left) @@ -180,27 +172,27 @@ class PrecompiledContract: gas_left: remaining gas, 0 on error """ # Check gas - if gas < self.GAS_COST: + if gas < self.PRECOMPILE_GAS_COST: return b"insufficient gas", 0 - # Check static call - if static: - return b"cannot call in static context", 0 + # Check STATICCALL + if read_only: + return b"STATICCALL disallows state modification", 0 # Get and validate caller's code code = state_db.get_code(caller) if not code.startswith(self.DELEGATED_CODE_PREFIX): return b"invalid delegated code prefix", 0 - # Update code based on length + # Update delegated code based on length if len(code) == 23: # Active state state_db.set_code(caller, code + b"\x00") # Deactivate elif len(code) == 24: # Deactivated state state_db.set_code(caller, code[:-1]) # Activate else: # Although this is not possible, it's added for completeness - return b"invalid code length", 0 + return b"invalid delegated code length", 0 - return b"", gas - self.GAS_COST + return b"", gas - self.PRECOMPILE_GAS_COST class StateDB: """Simplified state database, omitting other account fields""" @@ -240,7 +232,7 @@ This EIP does not revoke [ERC-2612](./eip-2612) permissions. EOAs with deactivat For deactivation through EOA-signed transactions, the replay protection mechanism provided by [EIP-155](./eip-155), if enabled, can effectively prevent cross-chain message replay. -For deactivation/reactivation called by the delegated contract, the contract should ensure that the chain ID is part of the message validation process (or implement alternative replay protection mechanisms) to prevent cross-chain message replay. +For deactivation or reactivation called by the delegated contract, the contract should ensure that the chain ID is part of the message validation process (or implement alternative replay protection mechanisms) to prevent cross-chain message replay. ## Copyright