From 99cc1bcb0a874ce4d72900d3bd1e426f7eb31606 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 05:14:18 +0800 Subject: [PATCH 01/22] Create eip-xxxx.md --- EIPS/eip-xxxx.md | 201 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 EIPS/eip-xxxx.md diff --git a/EIPS/eip-xxxx.md b/EIPS/eip-xxxx.md new file mode 100644 index 00000000000000..cb4a5edf437fb5 --- /dev/null +++ b/EIPS/eip-xxxx.md @@ -0,0 +1,201 @@ +--- +title: Private key deactivation and reactivation +description: Introduce a new precompiled contract to enable Externally Owned Accounts (EOAs) to deactivate and reactivate their private keys. +author: Liyi Guo (@colinlyguo) +discussions-to: +status: Draft +type: Standards Track +category: Core +created: 2024-12-27 +requires: 20, 2612, 7701, 7702 +--- + +## Abstract + +This EIP introduces a precompiled contract that enables EOAs with delegated control to smart contracts via [EIP-7702](./eip-7702) to deactivate/reactivate their private keys. This design does not require additional storage fields or account state changes. By leveraging delegated code, reactivation can be performed securely through mechanisms such as social recovery. + +## Motivation + +[EIP-7702](./eip-7702) enables EOAs to gain smart contract capabilities, but the private key of the EOA still retains full control over the account. + +With this EIP, EOAs can fully migrate to smart contract wallets, while retaining recovery options with reactivation. The flexible deactivate/reactivate design also paves the way for native account abstraction. e.g., [EIP-7701](./eip-7701). + +## Specification + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. + +### Parameters + +| Constant | Value | +|-----------------------------------|----------------------| +| `PRECOMPILE_ADDRESS` | `0xTBD` | +| `PRECOMPILE_GAS_COST` | `5000` (tentative) | + +### Delegated code encoding + +The deactivation status is encoded by appending or removing the `0x00` byte at the end of the delegated code. The transitions between two states are as follows: + +- Active state: `0xef0100 || address`, the private key is active and can sign transactions. +- Deactivated state: `0xef0100 || address || 0x00`, the private key is deactivated and cannot sign transactions. + +### Precompiled contract + +A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. For each call, it consumes `PRECOMPILE_GAS_COST` gas, and the precompiled contract executes the following steps: +- The precompiled contract checks that the caller is an EOA with delegated code (i.e., its account code begins with the prefix `0xef0100`, as defined in [EIP-7702](./eip-7702)). If the code does not conform to the required prefix, the contract MUST terminate without making any state changes. +- The precompile determines the current state of the delegated code based on its byte length: + - If the delegated code is 24 bytes (`0xef0100 || address || 0x00`), it removes the last byte (`0x00`), transitioning to the active state (`0xef0100 || address`). + - If the delegated code is 23 bytes (`0xef0100 || address`), it appends `0x00`, transitioning to the deactivated state (`0xef0100 || address || 0x00`). +- The updated delegated code is saved as the new account code for the EOA. + + +### Transaction validation +If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). + +### Gas cost +No changes to the base transaction gas cost (`21000`) are required, as the additional valid check for the deactivation status is minimal compared with [EIP-7702](./eip-7702) transactions. Thus it is reasonable to include them in the base gas cost. + +## Rationale + +### Using a precompiled contract +Alternative methods for implementing this feature include: +- Adding a new transaction type: A new transaction type could deactivate/reactive EOA private keys. This would complicate reactivation, as the contract would need to serve as the authorizer for reactivation, increasing protocol complexity. +- Deploying a regular smart contract: A regular deployed contract could track the `deactivated` status of each `address`. This approach would break the base transaction gas cost of `21000` in transaction validation, as accessing the `deactivated` status would require additional address and storage lookups, increasing gas usage. + +### In-protocol reactivation +This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g., to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. + +### `5000` Gas `PRECOMPILE_GAS_COST` +The `5000` gas cost is sufficient to cover validation, computation, and storage updates for the delegated code. + +### Alternative EOA migration approach +One alternative migration approach involves using a hard fork to edit all existing and new EOAs to upgradable smart contracts using EOA's ECDSA signatures. Users can then upgrade these smart contracts to achieve more granular permission control. However, this approach is incompatible with EOAs that have already delegated to smart contracts, as it overwrites the existing smart contract implementations. The 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 future prefixes such as `0xef0101` are introduced, changing the prefix (e.g., to `0xef01ff`) makes it unclear which prefix to restore upon reactivation. + +### Avoiding account state changes +Another alternative is to add a new field to the account state to store the `deactivated` status. However, this approach complicates the account state. It also brings changes in the account trie structure and RLP encoding used in networking, which complicates the implementation. + +### Forwards compatibility for removing EOAs +After all existing and future EOAs have been migrated to smart contracts. It's natural and easy to deprecate this EIP: +- Removing the precompiled contract. +- Removing validation of the deactivation status since all EOAs are smart contracts. +- The appended `0x00` byte can be optionally removed from the delegated code. + +## Backwards Compatibility + +This EIP maintains backwards compatibility with existing EOAs and contracts. + +## Test Cases + +```python +# Initialize the state database and precompiled contract +state_db = StateDB() +precompiled_contract = PrecompiledContract() + +# Test 1: Valid caller with active state +valid_caller = "0x1234" +delegated_address = bytes.fromhex("112233445566778899aabbccddeeff0011223344") +delegated_code = PrecompiledContract.DELEGATED_CODE_PREFIX + delegated_address +state_db.set_code(valid_caller, delegated_code) # Active state +assert state_db.get_code(valid_caller) == delegated_code # Verify initial state + +# Toggle to deactivated state +precompiled_contract.run(valid_caller, state_db) +assert state_db.get_code(valid_caller) == delegated_code + b"\x00" # Verify deactivated + +# Toggle back to active state +precompiled_contract.run(valid_caller, state_db) +assert state_db.get_code(valid_caller) == delegated_code # Verify reactivated + +# Test 2: Invalid caller without delegated code +invalid_caller = "0x5678" # No delegated code +assert state_db.get_code(invalid_caller) == b"" # Verify initial state is empty + +# Run contract on invalid caller (should do nothing) +precompiled_contract.run(invalid_caller, state_db) +assert state_db.get_code(invalid_caller) == b"" # State remains unchanged + +# Test 3: Caller with invalid prefix +invalid_prefix_caller = "0x9999" +invalid_code = bytes.fromhex("ab01") + delegated_address # Invalid prefix +state_db.set_code(invalid_prefix_caller, invalid_code) +precompiled_contract.run(invalid_prefix_caller, state_db) +assert state_db.get_code(invalid_prefix_caller) == invalid_code # State remains unchanged +``` + +## Reference Implementation + +```python +class PrecompiledContract: + """ + Precompiled contract for toggling the activation status of EOAs + based on their delegated code. + """ + DELEGATED_CODE_PREFIX = bytes.fromhex("ef0100") # Prefix for delegated code + ACTIVE_CODE_LENGTH = 23 # Length of code in active state + DEACTIVATED_CODE_LENGTH = 24 # Length of code in deactivated state + + def run(self, caller, state_db): + """ + Toggles the delegated code of the caller between active and deactivated states. + + Parameters: + - caller: The address calling the contract. + - state_db: The state database containing account states. + """ + # Retrieve the current code of the caller + code = state_db.get_code(caller) + + # Validate the code prefix + if not code.startswith(self.DELEGATED_CODE_PREFIX): + return # If it's not an EOA with valid delegated code, terminate with no changes + + # Determine the current state based on code length + if len(code) == self.DEACTIVATED_CODE_LENGTH: # Deactivated state (ends with 0x00) + state_db.set_code(caller, code[:-1]) # Remove the last byte to activate + return + + if len(code) == self.ACTIVE_CODE_LENGTH: # Activated state + state_db.set_code(caller, code + b"\x00") # Append 0x00 to deactivate + return + + # The case should not occur, this is for completeness sake + return + +class StateDB: + """ + Simplified state database for managing EOA states. + Other fields (e.g., `nonce`, `codehash`, `balance`, and `storageRoot`) are omitted for simplicity. + """ + def __init__(self): + self.accounts = {} + + def get_code(self, addr): + return self.accounts.get(addr, {}).get("code", b"") + + def set_code(self, addr, value): + if addr not in self.accounts: + self.accounts[addr] = {} + self.accounts[addr]["code"] = value +``` + +## Security Considerations + +### Unchanged gas consumption for transactions +This EIP does not introduce additional gas costs for transactions. The validation of the deactivation status is performed by checking the presence of the appended `0x00` byte in the account's delegated code. This check is computationally lightweight compared to operations like accessing storage. + +### Additional status check during transaction validation +The deactivation status is determined by checking the length of the delegated code. This check is computationally trivial and comparable to other account state checks, such as nonce and balance validation. Since it is integrated into the transaction validation process, it does not introduce any significant additional computational overhead. + +### Risk of asset freezing +For a malicious wallet, it could deliberately deactivate the account and block reactivation, effectively freezing assets. In this case, the risk is inherent to delegating control and not caused by this protocol. + +The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks by using thoroughly audited wallets that fully support this EIP. + +### Permit extension for [ERC-20](./eip20) +This EIP does not revoke [ERC-2612](./eip2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip20) tokens. This issue also exists with [EIP-7702](./eip7702). If wanting to support deactivated EOAs, [ERC-20](./eip20) contracts may need to be upgraded. + +## Copyright + +Copyright and related rights waived via [CC0](../LICENSE.md). From 314ed2b98598f5ad51c5c1a4277686ca232c1df9 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 08:15:23 +0800 Subject: [PATCH 02/22] update eip number, filename and discussions-to link --- EIPS/{eip-xxxx.md => eip-7851.md} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename EIPS/{eip-xxxx.md => eip-7851.md} (96%) diff --git a/EIPS/eip-xxxx.md b/EIPS/eip-7851.md similarity index 96% rename from EIPS/eip-xxxx.md rename to EIPS/eip-7851.md index cb4a5edf437fb5..69d4b90fee11eb 100644 --- a/EIPS/eip-xxxx.md +++ b/EIPS/eip-7851.md @@ -1,8 +1,9 @@ --- -title: Private key deactivation and reactivation -description: Introduce a new precompiled contract to enable Externally Owned Accounts (EOAs) to deactivate and reactivate their private keys. +eip: 7851 +title: Private key deactivation and reactivation of EOAs with delegated code +description: Introduce a new precompiled contract that enables Externally Owned Accounts (EOAs) with delegated code to deactivate and reactivate their private keys. author: Liyi Guo (@colinlyguo) -discussions-to: +discussions-to: https://ethereum-magicians.org/t/eip-7851-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 status: Draft type: Standards Track category: Core From 9c7667a2af7dc506b0af6732f9a21fe83600d706 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 08:20:43 +0800 Subject: [PATCH 03/22] shorten title and description --- EIPS/eip-7851.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 69d4b90fee11eb..e20579c2cde0c2 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,7 +1,7 @@ --- eip: 7851 -title: Private key deactivation and reactivation of EOAs with delegated code -description: Introduce a new precompiled contract that enables Externally Owned Accounts (EOAs) with delegated code to deactivate and reactivate their private keys. +title: EOA private key deactivation/reactivation +description: Introduce a precompiled contract for EOAs with delegated code to deactivate/reactivate private keys. author: Liyi Guo (@colinlyguo) discussions-to: https://ethereum-magicians.org/t/eip-7851-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 status: Draft @@ -13,7 +13,7 @@ requires: 20, 2612, 7701, 7702 ## Abstract -This EIP introduces a precompiled contract that enables EOAs with delegated control to smart contracts via [EIP-7702](./eip-7702) to deactivate/reactivate their private keys. This design does not require additional storage fields or account state changes. By leveraging delegated code, reactivation can be performed securely through mechanisms such as social recovery. +This EIP introduces a precompiled contract that enables Externally Owned Accounts (EOAs) with delegated control to smart contracts via [EIP-7702](./eip-7702) to deactivate/reactivate their private keys. This design does not require additional storage fields or account state changes. By leveraging delegated code, reactivation can be performed securely through mechanisms such as social recovery. ## Motivation From 92f67328160e81ca3c67e3ddda669d22ebc07722 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 08:30:08 +0800 Subject: [PATCH 04/22] fix Markdown Linter --- EIPS/eip-7851.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index e20579c2cde0c2..030b6a572bdbe0 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,7 +1,7 @@ --- eip: 7851 title: EOA private key deactivation/reactivation -description: Introduce a precompiled contract for EOAs with delegated code to deactivate/reactivate private keys. +description: Introduce a new precompiled contract for EOAs with delegated code to deactivate/reactivate private keys. author: Liyi Guo (@colinlyguo) discussions-to: https://ethereum-magicians.org/t/eip-7851-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 status: Draft @@ -42,43 +42,54 @@ The deactivation status is encoded by appending or removing the `0x00` byte at t ### Precompiled contract A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. For each call, it consumes `PRECOMPILE_GAS_COST` gas, and the precompiled contract executes the following steps: + - The precompiled contract checks that the caller is an EOA with delegated code (i.e., its account code begins with the prefix `0xef0100`, as defined in [EIP-7702](./eip-7702)). If the code does not conform to the required prefix, the contract MUST terminate without making any state changes. - The precompile determines the current state of the delegated code based on its byte length: - - If the delegated code is 24 bytes (`0xef0100 || address || 0x00`), it removes the last byte (`0x00`), transitioning to the active state (`0xef0100 || address`). - - If the delegated code is 23 bytes (`0xef0100 || address`), it appends `0x00`, transitioning to the deactivated state (`0xef0100 || address || 0x00`). + - If the delegated code is 24 bytes (`0xef0100 || address || 0x00`), it removes the last byte (`0x00`), transitioning to the active state (`0xef0100 || address`). + - If the delegated code is 23 bytes (`0xef0100 || address`), it appends `0x00`, transitioning to the deactivated state (`0xef0100 || address || 0x00`). - The updated delegated code is saved as the new account code for the EOA. - ### Transaction validation + If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). ### Gas cost + No changes to the base transaction gas cost (`21000`) are required, as the additional valid check for the deactivation status is minimal compared with [EIP-7702](./eip-7702) transactions. Thus it is reasonable to include them in the base gas cost. ## Rationale ### Using a precompiled contract + Alternative methods for implementing this feature include: + - Adding a new transaction type: A new transaction type could deactivate/reactive EOA private keys. This would complicate reactivation, as the contract would need to serve as the authorizer for reactivation, increasing protocol complexity. - Deploying a regular smart contract: A regular deployed contract could track the `deactivated` status of each `address`. This approach would break the base transaction gas cost of `21000` in transaction validation, as accessing the `deactivated` status would require additional address and storage lookups, increasing gas usage. ### In-protocol reactivation + This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g., to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. ### `5000` Gas `PRECOMPILE_GAS_COST` + The `5000` gas cost is sufficient to cover validation, computation, and storage updates for the delegated code. ### Alternative EOA migration approach + One alternative migration approach involves using a hard fork to edit all existing and new EOAs to upgradable smart contracts using EOA's ECDSA signatures. Users can then upgrade these smart contracts to achieve more granular permission control. However, this approach is incompatible with EOAs that have already delegated to smart contracts, as it overwrites the existing smart contract implementations. The 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 future prefixes such as `0xef0101` are introduced, changing the prefix (e.g., to `0xef01ff`) makes it unclear which prefix to restore upon reactivation. ### Avoiding account state changes + Another alternative is to add a new field to the account state to store the `deactivated` status. However, this approach complicates the account state. It also brings changes in the account trie structure and RLP encoding used in networking, which complicates the implementation. ### Forwards compatibility for removing EOAs + After all existing and future EOAs have been migrated to smart contracts. It's natural and easy to deprecate this EIP: + - Removing the precompiled contract. - Removing validation of the deactivation status since all EOAs are smart contracts. - The appended `0x00` byte can be optionally removed from the delegated code. @@ -184,17 +195,21 @@ class StateDB: ## Security Considerations ### Unchanged gas consumption for transactions + This EIP does not introduce additional gas costs for transactions. The validation of the deactivation status is performed by checking the presence of the appended `0x00` byte in the account's delegated code. This check is computationally lightweight compared to operations like accessing storage. ### Additional status check during transaction validation + The deactivation status is determined by checking the length of the delegated code. This check is computationally trivial and comparable to other account state checks, such as nonce and balance validation. Since it is integrated into the transaction validation process, it does not introduce any significant additional computational overhead. ### Risk of asset freezing + For a malicious wallet, it could deliberately deactivate the account and block reactivation, effectively freezing assets. In this case, the risk is inherent to delegating control and not caused by this protocol. The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks by using thoroughly audited wallets that fully support this EIP. ### Permit extension for [ERC-20](./eip20) + This EIP does not revoke [ERC-2612](./eip2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip20) tokens. This issue also exists with [EIP-7702](./eip7702). If wanting to support deactivated EOAs, [ERC-20](./eip20) contracts may need to be upgraded. ## Copyright From dd2586e97e1cbadab80ea19963b73c906588bc7b Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 08:49:13 +0800 Subject: [PATCH 05/22] fix internally linking --- EIPS/eip-7851.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 030b6a572bdbe0..2791e90d292728 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -68,7 +68,7 @@ Alternative methods for implementing this feature include: ### In-protocol reactivation -This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g., to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. +This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip-7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g., to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. ### `5000` Gas `PRECOMPILE_GAS_COST` @@ -208,9 +208,9 @@ For a malicious wallet, it could deliberately deactivate the account and block r The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks by using thoroughly audited wallets that fully support this EIP. -### Permit extension for [ERC-20](./eip20) +### Permit extension for [ERC-20](./eip-20) -This EIP does not revoke [ERC-2612](./eip2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip20) tokens. This issue also exists with [EIP-7702](./eip7702). If wanting to support deactivated EOAs, [ERC-20](./eip20) contracts may need to be upgraded. +This EIP does not revoke [ERC-2612](./eip-2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip-20) tokens. This issue also exists with [EIP-7702](./eip-7702). If wanting to support deactivated EOAs, [ERC-20](./eip-20) contracts may need to be upgraded. ## Copyright From 41309220aa9d47043b09e02943ea2542ea26cbd4 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Fri, 27 Dec 2024 09:27:00 +0800 Subject: [PATCH 06/22] Message replay across EVM-compatible chains --- EIPS/eip-7851.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 2791e90d292728..0eadff98358235 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -212,6 +212,12 @@ The risk also exists when the delegated wallet does not support reactivation or This EIP does not revoke [ERC-2612](./eip-2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip-20) tokens. This issue also exists with [EIP-7702](./eip-7702). If wanting to support deactivated EOAs, [ERC-20](./eip-20) contracts may need to be upgraded. +### Message replay across EVM-compatible chains + +For deactivation enabled by EOA signed transactions, the replay protection mechanism provided by [EIP-155](./eip-155), if enabled, can effectively prevent cross-chain message replay. + +For contract-based deactivation/reactivation, 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 Copyright and related rights waived via [CC0](../LICENSE.md). From 0f2aabaad752324ad8bab51a123213ca290a1c10 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 29 Dec 2024 04:44:46 +0800 Subject: [PATCH 07/22] add EIP-7702 authorization check and precompile revert details --- EIPS/eip-7851.md | 175 ++++++++++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 77 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 0eadff98358235..fcbd0a03e406bc 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,7 +1,7 @@ --- eip: 7851 -title: EOA private key deactivation/reactivation -description: Introduce a new precompiled contract for EOAs with delegated code to deactivate/reactivate private keys. +title: EOA private key deactivation and reactivation +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-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 status: Draft @@ -13,13 +13,13 @@ requires: 20, 2612, 7701, 7702 ## Abstract -This EIP introduces a precompiled contract that enables Externally Owned Accounts (EOAs) with delegated control to smart contracts via [EIP-7702](./eip-7702) to deactivate/reactivate their private keys. This design does not require additional storage fields or account state changes. By leveraging delegated code, reactivation can be performed securely through mechanisms such as social recovery. +This EIP introduces a precompiled contract that enables Externally Owned Accounts (EOAs) with delegated control to smart contracts via [EIP-7702](./eip-7702) to deactivate or reactivate their private keys. This design does not require additional storage fields or account state changes. By leveraging delegated code, reactivation can be performed securely through mechanisms such as social recovery. ## Motivation [EIP-7702](./eip-7702) enables EOAs to gain smart contract capabilities, but the private key of the EOA still retains full control over the account. -With this EIP, EOAs can fully migrate to smart contract wallets, while retaining recovery options with reactivation. The flexible deactivate/reactivate design also paves the way for native account abstraction. e.g., [EIP-7701](./eip-7701). +With this EIP, EOAs can fully migrate to smart contract wallets, while retaining recovery options with reactivation. The flexible deactivate and reactivate design also paves the way for native account abstraction. e.g. [EIP-7701](./eip-7701). ## Specification @@ -41,21 +41,29 @@ The deactivation status is encoded by appending or removing the `0x00` byte at t ### Precompiled contract -A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. For each call, it consumes `PRECOMPILE_GAS_COST` gas, and the precompiled contract executes the following steps: +A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It costs `POINT_EVALUATION_PRECOMPILE_GAS` and executes the following logic: -- The precompiled contract checks that the caller is an EOA with delegated code (i.e., its account code begins with the prefix `0xef0100`, as defined in [EIP-7702](./eip-7702)). If the code does not conform to the required prefix, the contract MUST terminate without making any state changes. -- The precompile determines the current state of the delegated code based on its byte length: - - If the delegated code is 24 bytes (`0xef0100 || address || 0x00`), it removes the last byte (`0x00`), transitioning to the active state (`0xef0100 || address`). - - If the delegated code is 23 bytes (`0xef0100 || address`), it appends `0x00`, transitioning to the deactivated state (`0xef0100 || address || 0x00`). -- The updated delegated code is saved as the new account code for the EOA. +- Returns a precompile contract error and consumes all provided gas if: + - Gas is insufficient. + - Called via `STATICCALL` (i.e. in a read-only context). + - Caller is not an EOA with delegated code (prefix `0xef0100` as per [EIP-7702](./eip-7702)). +- Updates caller's delegated code based on length: + - 23 bytes (`0xef0100 || address`): Appends `0x00` to deactivate private key authorization. + - 24 bytes (`0xef0100 || address || 0x00`): Removes last byte `0x00` to activate private key authorization. +- Saves updated code as caller's new account code. ### Transaction validation -If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). +If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), the following validations MUST be performed: -### Gas cost +- Transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). +- Any [EIP-7702](./eip-7702) authorization from an authority with deactivated delegated code MUST be considered invalid and skipped. -No changes to the base transaction gas cost (`21000`) are required, as the additional valid check for the deactivation status is minimal compared with [EIP-7702](./eip-7702) transactions. Thus it is reasonable to include them in the base gas cost. +### Gas Cost + +No changes to the base transaction gas cost (`21000`) are required, as the additional valid check for the deactivation status is minimal. Thus it is reasonable to consider the overhead covered by the base 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. ## Rationale @@ -68,7 +76,7 @@ Alternative methods for implementing this feature include: ### In-protocol reactivation -This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip-7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g., to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. +This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip-7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g. to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. ### `5000` Gas `PRECOMPILE_GAS_COST` @@ -80,7 +88,7 @@ One alternative migration approach involves using a hard fork to edit all existi ### 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 future prefixes such as `0xef0101` are introduced, 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 future prefixes such as `0xef0101` are introduced, changing the prefix (e.g. to `0xef01ff`) makes it unclear which prefix to restore upon reactivation. ### Avoiding account state changes @@ -96,90 +104,103 @@ After all existing and future EOAs have been migrated to smart contracts. It's n ## Backwards Compatibility -This EIP maintains backwards compatibility with existing EOAs and contracts. +This EIP maintains backwards compatibility with existing EOAs and contracts. Additional checks are added after this EIP to reject: +- Transactions signed by an EOA with a deactivated private key. +- EIP-7702 authorizations from an EOA with a deactivated private key. ## Test Cases ```python # Initialize the state database and precompiled contract state_db = StateDB() -precompiled_contract = PrecompiledContract() - -# Test 1: Valid caller with active state -valid_caller = "0x1234" -delegated_address = bytes.fromhex("112233445566778899aabbccddeeff0011223344") -delegated_code = PrecompiledContract.DELEGATED_CODE_PREFIX + delegated_address -state_db.set_code(valid_caller, delegated_code) # Active state -assert state_db.get_code(valid_caller) == delegated_code # Verify initial state - -# Toggle to deactivated state -precompiled_contract.run(valid_caller, state_db) -assert state_db.get_code(valid_caller) == delegated_code + b"\x00" # Verify deactivated - -# Toggle back to active state -precompiled_contract.run(valid_caller, state_db) -assert state_db.get_code(valid_caller) == delegated_code # Verify reactivated - -# Test 2: Invalid caller without delegated code -invalid_caller = "0x5678" # No delegated code -assert state_db.get_code(invalid_caller) == b"" # Verify initial state is empty - -# Run contract on invalid caller (should do nothing) -precompiled_contract.run(invalid_caller, state_db) -assert state_db.get_code(invalid_caller) == b"" # State remains unchanged - -# Test 3: Caller with invalid prefix -invalid_prefix_caller = "0x9999" -invalid_code = bytes.fromhex("ab01") + delegated_address # Invalid prefix -state_db.set_code(invalid_prefix_caller, invalid_code) -precompiled_contract.run(invalid_prefix_caller, state_db) -assert state_db.get_code(invalid_prefix_caller) == invalid_code # State remains unchanged +precompile = PrecompiledContract() + +# Test 1: Valid activation and deactivation +caller = "0x0123" +delegated_addr = bytes.fromhex("1122334455667788990011223344556677889900") +active_code = PrecompiledContract.DELEGATED_CODE_PREFIX + delegated_addr + +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 + +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 + +# Test 2: Error cases +error, gas_left = precompile.execute(caller, state_db, gas=10000, static=True) +assert error == b"cannot call in static context" +assert gas_left == 0 + +error, gas_left = precompile.execute(caller, state_db, gas=PrecompiledContract.GAS_COST-1) +assert error == b"insufficient gas" +assert gas_left == 0 + +# EOA without delegated code +caller = "0x4567" +error, gas_left = precompile.execute(caller, state_db, gas=10000) +assert error == b"invalid delegated code prefix" +assert gas_left == 0 + +# Small contract code +caller = "0x89ab" +state_db.set_code(caller, bytes.fromhex("00")) # a contract with a single STOP opcode +error, gas_left = precompile.execute(caller, state_db, gas=10000) +assert error == b"invalid delegated code prefix" +assert gas_left == 0 ``` ## Reference Implementation ```python class PrecompiledContract: - """ - Precompiled contract for toggling the activation status of EOAs - based on their delegated code. - """ - DELEGATED_CODE_PREFIX = bytes.fromhex("ef0100") # Prefix for delegated code - ACTIVE_CODE_LENGTH = 23 # Length of code in active state - DEACTIVATED_CODE_LENGTH = 24 # Length of code in deactivated state - - def run(self, caller, state_db): + DELEGATED_CODE_PREFIX = bytes.fromhex("ef0100") # EIP-7702 prefix + GAS_COST = 5000 # PRECOMPILE_GAS_COST + + def execute(self, caller, state_db, gas, static=False): """ - Toggles the delegated code of the caller between active and deactivated states. + Toggle EOA's private key authorization between active/deactivated states. Parameters: - - caller: The address calling the contract. - - state_db: The state database containing account states. + - caller: The address calling the contract + - state_db: The state database + - gas: Gas provided for execution + - static: Whether called in read-only context + + Returns: + - Tuple of (result, gas_left) + result: error bytes on failure, empty bytes on success + gas_left: remaining gas, 0 on error """ - # Retrieve the current code of the caller - code = state_db.get_code(caller) + # Check gas + if gas < self.GAS_COST: + return b"insufficient gas", 0 + + # Check static call + if static: + return b"cannot call in static context", 0 - # Validate the code prefix + # Get and validate caller's code + code = state_db.get_code(caller) if not code.startswith(self.DELEGATED_CODE_PREFIX): - return # If it's not an EOA with valid delegated code, terminate with no changes + return b"invalid delegated code prefix", 0 - # Determine the current state based on code length - if len(code) == self.DEACTIVATED_CODE_LENGTH: # Deactivated state (ends with 0x00) - state_db.set_code(caller, code[:-1]) # Remove the last byte to activate - return - - if len(code) == self.ACTIVE_CODE_LENGTH: # Activated state - state_db.set_code(caller, code + b"\x00") # Append 0x00 to deactivate - return + # Update 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 - # The case should not occur, this is for completeness sake - return + return b"", gas - self.GAS_COST class StateDB: - """ - Simplified state database for managing EOA states. - Other fields (e.g., `nonce`, `codehash`, `balance`, and `storageRoot`) are omitted for simplicity. - """ + """Simplified state database, omitting other account fields""" def __init__(self): self.accounts = {} From e8778dc2469774177d909bedf96050417a78ea68 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 29 Dec 2024 04:48:34 +0800 Subject: [PATCH 08/22] shorten title --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index fcbd0a03e406bc..df367a8eadaaa1 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,6 +1,6 @@ --- eip: 7851 -title: EOA private key deactivation and reactivation +title: EOA private key deactivation/reactivation 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-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 From f817605f61e4fdd9e1d6d1f9a4e7639e74eb8f7b Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 29 Dec 2024 05:05:28 +0800 Subject: [PATCH 09/22] fix errors reported by markdown linter --- EIPS/eip-7851.md | 1 + 1 file changed, 1 insertion(+) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index df367a8eadaaa1..d1a58f962b7cc7 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -105,6 +105,7 @@ After all existing and future EOAs have been migrated to smart contracts. It's n ## Backwards Compatibility This EIP maintains backwards compatibility with existing EOAs and contracts. Additional checks are added after this EIP to reject: + - Transactions signed by an EOA with a deactivated private key. - EIP-7702 authorizations from an EOA with a deactivated private key. From 5ca26332c3ea10dc33b1e613841a772e779c4162 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 29 Dec 2024 05:11:14 +0800 Subject: [PATCH 10/22] update migicians link to align title --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index d1a58f962b7cc7..715438017d1489 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -3,7 +3,7 @@ eip: 7851 title: EOA private key deactivation/reactivation 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-private-key-deactivation-and-reactivation-of-eoas-with-delegated-code/22344 +discussions-to: https://ethereum-magicians.org/t/eip-7851-eoa-private-key-deactivation-reactivation/22344 status: Draft type: Standards Track category: Core From 6b6f5bfa8a63458da3829dfed13ab102dd5cb4bd Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 29 Dec 2024 15:35:06 +0800 Subject: [PATCH 11/22] rephrase --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 715438017d1489..75db8b25479ff0 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -43,7 +43,7 @@ The deactivation status is encoded by appending or removing the `0x00` byte at t A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It costs `POINT_EVALUATION_PRECOMPILE_GAS` and executes the following logic: -- Returns a precompile contract error and consumes all provided gas if: +- Returns a precompile contract error, consumes all gas, and no state changes are made if: - Gas is insufficient. - Called via `STATICCALL` (i.e. in a read-only context). - Caller is not an EOA with delegated code (prefix `0xef0100` as per [EIP-7702](./eip-7702)). From 83278f7ac401974a8d9747b9ec4974a8c31a37be Mon Sep 17 00:00:00 2001 From: colin <102356659+colinlyguo@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:23:30 +0800 Subject: [PATCH 12/22] Update EIPS/eip-7851.md Co-authored-by: Jochem Brouwer --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 75db8b25479ff0..03ca11eb62868c 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -8,7 +8,7 @@ status: Draft type: Standards Track category: Core created: 2024-12-27 -requires: 20, 2612, 7701, 7702 +requires: 7702 --- ## Abstract From 7f93c930ec48d76380eeb8f82748e5a65a040d62 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 31 Dec 2024 04:48:12 +0800 Subject: [PATCH 13/22] clarifications and fixes in transaction validation and related discussions --- EIPS/eip-7851.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 03ca11eb62868c..e1c4b6f564ffe7 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -56,13 +56,12 @@ A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It cos If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), the following validations MUST be performed: -- Transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). -- Any [EIP-7702](./eip-7702) authorization from an authority with deactivated delegated code MUST be considered invalid and skipped. +- During the consensus rule's transaction validity check before execution: transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). This ensures such transactions cannot be included in blocks. +- The transaction pool MUST implement the same validation to prevent invalid transactions from being propagated 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 -No changes to the base transaction gas cost (`21000`) are required, as the additional valid check for the deactivation status is minimal. Thus it is reasonable to consider the overhead covered by the base 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. ## Rationale @@ -104,10 +103,7 @@ After all existing and future EOAs have been migrated to smart contracts. It's n ## Backwards Compatibility -This EIP maintains backwards compatibility with existing EOAs and contracts. Additional checks are added after this EIP to reject: - -- Transactions signed by an EOA with a deactivated private key. -- EIP-7702 authorizations from an EOA with a deactivated private key. +This EIP maintains backwards compatibility with existing EOAs and contracts. ## Test Cases @@ -216,13 +212,13 @@ class StateDB: ## Security Considerations -### Unchanged gas consumption for transactions +### Additional checks during transaction validation -This EIP does not introduce additional gas costs for transactions. The validation of the deactivation status is performed by checking the presence of the appended `0x00` byte in the account's delegated code. This check is computationally lightweight compared to operations like accessing storage. +The deactivation status is determined by checking the length of the delegated code. This check introduces an additional storage read, comparable to account state read and checks, such as nonce and balance validation. It is integrated into the transaction validity check of the transaction pool and consensus rules, ensuring that invalid transactions are filtered out before propagation and execution. -### Additional status check during transaction validation +### Additional checks for [EIP-7702](./eip-7702) transactions -The deactivation status is determined by checking the length of the delegated code. This check is computationally trivial and comparable to other account state checks, such as nonce and balance validation. Since it is integrated into the transaction validation process, it does not introduce any significant additional computational overhead. +For [EIP-7702](./eip-7702) transactions, an additional check is introduced to validate the length of the delegated code. Since EIP-7702 already requires retrieving the code in authorization validation, the additional cost of verifying its length is negligible. ### Risk of asset freezing From 8cc68c63d827228525e440ca285bd28cdbd23f71 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 31 Dec 2024 17:31:56 +0800 Subject: [PATCH 14/22] rephrase alternative Deploying a regular smart contract in Rationale --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index e1c4b6f564ffe7..b88fa019282359 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -71,7 +71,7 @@ The `PER_EMPTY_ACCOUNT_COST` and `PER_AUTH_BASE_COST` constants defined in [EIP- Alternative methods for implementing this feature include: - Adding a new transaction type: A new transaction type could deactivate/reactive EOA private keys. This would complicate reactivation, as the contract would need to serve as the authorizer for reactivation, increasing protocol complexity. -- Deploying a regular smart contract: A regular deployed contract could track the `deactivated` status of each `address`. This approach would break the base transaction gas cost of `21000` in transaction validation, as accessing the `deactivated` status would require additional address and storage lookups, increasing gas usage. +- Deploying a regular smart contract: A regular deployed contract could track the `deactivated` status of each `address`. During deactivation and reactivation, invoking this contract, which executes bytecode and accesses storage, would be more expensive than a precompiled contract. Also, this method "leaks" the design of a widely used programming language to Ethereum core protocol, which is not ideal, though there is no obvious security risk to do so. ### In-protocol reactivation From 35ec718df5cd6726b7f723a8c3db3c590fd3875b Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 31 Dec 2024 17:35:52 +0800 Subject: [PATCH 15/22] rephrase --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index b88fa019282359..f2e45a07657ecd 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -71,7 +71,7 @@ The `PER_EMPTY_ACCOUNT_COST` and `PER_AUTH_BASE_COST` constants defined in [EIP- Alternative methods for implementing this feature include: - Adding a new transaction type: A new transaction type could deactivate/reactive EOA private keys. This would complicate reactivation, as the contract would need to serve as the authorizer for reactivation, increasing protocol complexity. -- Deploying a regular smart contract: A regular deployed contract could track the `deactivated` status of each `address`. During deactivation and reactivation, invoking this contract, which executes bytecode and accesses storage, would be more expensive than a precompiled contract. Also, this method "leaks" the design of a widely used programming language to Ethereum core protocol, which is not ideal, though there is no obvious security risk to do so. +- Deploying a regular smart contract: A regular deployed contract could track the deactivated status of each address. However, during deactivation and reactivation, invoking this contract, which executes bytecode and accesses storage, would be significantly more expensive than using a precompiled contract. Furthermore, this approach introduces a subtle drawback as it "leaks" the design of a widely used programming language into the Ethereum core protocol. While this poses no obvious security risks, it is not ideal from a design perspective. ### In-protocol reactivation From 20237771a87e43729ec7c86b350cbfa75d860539 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Tue, 31 Dec 2024 17:40:47 +0800 Subject: [PATCH 16/22] rephrase Rationale - Avoiding account state changes --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index f2e45a07657ecd..f7dce15caf2fd4 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -91,7 +91,7 @@ This EIP appends a byte (`0x00`) to the delegated code instead of modifying the ### Avoiding account state changes -Another alternative is to add a new field to the account state to store the `deactivated` status. However, this approach complicates the account state. It also brings changes in the account trie structure and RLP encoding used in networking, which complicates the implementation. +Another alternative is to add a bool field `deactivated` in the account state to track the status. However, this approach complicates the account state. It also complicates the implementation of the account trie structure and RLP encoding used in networking, e.g. adding upgrade code. ### Forwards compatibility for removing EOAs From 8f3f7f4c806b160c4986cdca05ef706ca32f570d Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Wed, 1 Jan 2025 19:41:25 +0800 Subject: [PATCH 17/22] fixes and tweaks --- EIPS/eip-7851.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index f7dce15caf2fd4..a0dcfe014e37c9 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -19,7 +19,7 @@ This EIP introduces a precompiled contract that enables Externally Owned Account [EIP-7702](./eip-7702) enables EOAs to gain smart contract capabilities, but the private key of the EOA still retains full control over the account. -With this EIP, EOAs can fully migrate to smart contract wallets, while retaining recovery options with reactivation. The flexible deactivate and reactivate design also paves the way for native account abstraction. e.g. [EIP-7701](./eip-7701). +With this EIP, EOAs can fully migrate to smart contract wallets, while retaining private key recovery options with reactivation. The flexible deactivate and reactivate design also paves the way for native account abstraction. e.g. [EIP-7701](./eip-7701). ## Specification @@ -36,12 +36,12 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S The deactivation status is encoded by appending or removing the `0x00` byte at the end of the delegated code. The transitions between two states are as follows: -- Active state: `0xef0100 || address`, the private key is active and can sign transactions. -- Deactivated state: `0xef0100 || address || 0x00`, the private key is deactivated and cannot sign transactions. +- Active state: `0xef0100 || address`. The private key is active and can sign transactions. +- Deactivated state: `0xef0100 || address || 0x00`. The private key is deactivated and cannot sign transactions or [EIP-7702](./eip-7702) delegation authorizations. ### Precompiled contract -A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It costs `POINT_EVALUATION_PRECOMPILE_GAS` and executes the following logic: +A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It costs `PRECOMPILE_GAS_COST` and executes the following logic: - Returns a precompile contract error, consumes all gas, and no state changes are made if: - Gas is insufficient. @@ -54,10 +54,10 @@ A new precompiled contract is introduced at address `PRECOMPILE_ADDRESS`. It cos ### Transaction validation -If the account is verified as an EOA with a delegated code (begins with the prefix `0xef0100`), the following validations MUST be performed: +For EOAs with delegated code (begins with the prefix `0xef0100`), the following validations MUST be performed: -- During the consensus rule's transaction validity check before execution: transactions signed by the private key MUST be rejected if the delegated code is in the deactivated state (i.e., `24` bytes long). This ensures such transactions cannot be included in blocks. -- The transaction pool MUST implement the same validation to prevent invalid transactions from being propagated across the network. +- During the consensus rule's transaction validity check before execution: transactions signed by the private key MUST be rejected if the delegated code indicates the private key is in the deactivated state (i.e., `24` bytes long). This ensures such transactions cannot be included in blocks. +- 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 From 81a6155b829d11a7bba8eed6e04ce328dee42dba Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 2 Jan 2025 00:19:29 +0800 Subject: [PATCH 18/22] tweak statements for clarification --- EIPS/eip-7851.md | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index a0dcfe014e37c9..c2989c5077ef62 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -70,36 +70,42 @@ The `PER_EMPTY_ACCOUNT_COST` and `PER_AUTH_BASE_COST` constants defined in [EIP- Alternative methods for implementing this feature include: -- Adding a new transaction type: A new transaction type could deactivate/reactive EOA private keys. This would complicate reactivation, as the contract would need to serve as the authorizer for reactivation, increasing protocol complexity. -- Deploying a regular smart contract: A regular deployed contract could track the deactivated status of each address. However, during deactivation and reactivation, invoking this contract, which executes bytecode and accesses storage, would be significantly more expensive than using a precompiled contract. Furthermore, this approach introduces a subtle drawback as it "leaks" the design of a widely used programming language into the Ethereum core protocol. While this poses no obvious security risks, it is not ideal from a design perspective. +- 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. ### In-protocol reactivation -This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip-7701) contract, and then deactivate their private keys again. This avoids the limitations of upgradable contracts. e.g. to remove legacy proxy contracts when EOF contracts become available, thereby reducing gas overhead, one can reactivate the EOA and delegate to an EOF proxy contract. +This approach ensures maximum compatibility with future migrations. EOAs can reactivate their private keys, delegate their accounts to an [EIP-7701](./eip-7701) contract, and then deactivate their private keys again. This avoids the limitations of contract upgrades. e.g. to remove legacy proxy contracts (reducing gas overhead) when upgrading to EOF contracts, one can reactivate the EOA and redelegate it to an EOF proxy contract. + +Reactivation can only be performed by the delegated contract. Since reactivating the private key grants full control over the wallet, including the ability to replace the delegated wallet, wallets must implement this interface with strict security measures. These measures should treat reactivation as the highest level of authority, equivalent to full ownership of the wallet. + +The reactivation process is recommended to include a signed authorization from the private key. This signature payload should consist of the chain ID and a random challenge (e.g. a hash value of other signatures) to ensure that: (i) the request is non-replayable, and (ii) the initiators contain the private key owner. + +Users should delegate their EOAs only to wallets that have been thoroughly audited and follow best practices for security. ### `5000` Gas `PRECOMPILE_GAS_COST` The `5000` gas cost is sufficient to cover validation, computation, and storage updates for the delegated code. -### Alternative EOA migration approach +### Alternative EOA deprecation approach -One alternative migration approach involves using a hard fork to edit all existing and new EOAs to upgradable smart contracts using EOA's ECDSA signatures. Users can then upgrade these smart contracts to achieve more granular permission control. However, this approach is incompatible with EOAs that have already delegated to smart contracts, as it overwrites 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 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. ### 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 future prefixes such as `0xef0101` are introduced, 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 (e.g. to `0xef01ff`) makes it unclear which prefix to restore 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 complicates the account state. It also complicates the implementation of the account trie structure and RLP encoding used in networking, e.g. adding upgrade code. +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 easy to deprecate this EIP: +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 validation of the deactivation status since all EOAs are smart contracts. -- The appended `0x00` byte can be optionally removed from the delegated code. +- 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. ## Backwards Compatibility @@ -145,7 +151,7 @@ assert gas_left == 0 # Small contract code caller = "0x89ab" -state_db.set_code(caller, bytes.fromhex("00")) # a contract with a single STOP opcode +state_db.set_code(caller, bytes.fromhex("00")) # a fake contract error, gas_left = precompile.execute(caller, state_db, gas=10000) assert error == b"invalid delegated code prefix" assert gas_left == 0 @@ -214,27 +220,27 @@ class StateDB: ### Additional checks during transaction validation -The deactivation status is determined by checking the length of the delegated code. This check introduces an additional storage read, comparable to account state read and checks, such as nonce and balance validation. It is integrated into the transaction validity check of the transaction pool and consensus rules, ensuring that invalid transactions are filtered out before propagation and execution. +The deactivation status is determined by checking the length of the delegated code. This check introduces an additional storage read and check comparable to account state read and checks, such as nonce validation. It is integrated into the transaction validity check of the transaction pool and consensus rules, ensuring that invalid transactions are filtered out before propagation and execution. ### Additional checks for [EIP-7702](./eip-7702) transactions -For [EIP-7702](./eip-7702) transactions, an additional check is introduced to validate the length of the delegated code. Since EIP-7702 already requires retrieving the code in authorization validation, the additional cost of verifying its length is negligible. +In applying authorizations of an [EIP-7702](./eip-7702) transaction, an additional check is introduced to validate the length of the delegated code. Since EIP-7702 already requires retrieving the code in authorization validation, the extra cost of verifying its length is negligible. ### Risk of asset freezing -For a malicious wallet, it could deliberately deactivate the account and block reactivation, effectively freezing assets. In this case, the risk is inherent to delegating control and not caused by this protocol. +A malicious wallet could deactivate the account and block reactivation, freezing assets. In this case, the risk is inherent to delegating control and not solely introduced by this EIP. -The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks by using thoroughly audited wallets that fully support this EIP. +The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks using thoroughly audited wallets that support this EIP. ### Permit extension for [ERC-20](./eip-20) -This EIP does not revoke [ERC-2612](./eip-2612) permissions. EOAs supporting this EIP can still authorize transfers by calling the `permit` function of [ERC-20](./eip-20) tokens. This issue also exists with [EIP-7702](./eip-7702). If wanting to support deactivated EOAs, [ERC-20](./eip-20) contracts may need to be upgraded. +This EIP does not revoke [ERC-2612](./eip-2612) permissions. EOAs with deactivated private keys can still authorize transfers by calling the `permit` function of [ERC-20](./eip-20) tokens supporting [ERC-2612](./eip-2612). This issue also exists with [EIP-7702](./eip-7702). To support deactivated EOAs, [ERC-20](./eip-20) contracts should deprecate the `permit` function, or a new opcode could be introduced to help check the `deactivated` status of the EOA. ### Message replay across EVM-compatible chains -For deactivation enabled by EOA signed transactions, the replay protection mechanism provided by [EIP-155](./eip-155), if enabled, can effectively prevent cross-chain message replay. +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 contract-based deactivation/reactivation, 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/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 From b2c95358dc977bbe0723367c4ecbce46d8a945f5 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 2 Jan 2025 00:27:39 +0800 Subject: [PATCH 19/22] tweaks --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index c2989c5077ef62..4953bc9591f5e9 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -228,7 +228,7 @@ In applying authorizations of an [EIP-7702](./eip-7702) transaction, an addition ### Risk of asset freezing -A malicious wallet could deactivate the account and block reactivation, freezing assets. In this case, the risk is inherent to delegating control and not solely introduced by this EIP. +In the worst case, a malicious wallet, through bypassing permission controls, could steal assets, and deactivate the account by calling the precompiled contract, it could also block reactivation, and thus freeze the account. In this case, the risk is inherent to delegating control and not solely introduced by this EIP. The risk also exists when the delegated wallet does not support reactivation or implements a flawed reactivation interface, combined with partially functional or non-functional asset transfers. These issues could prevent the user from reactivating the account and result in partial or complete asset freezing. Users can mitigate these risks using thoroughly audited wallets that support this EIP. From 57f71b970604a91e25dde0a23fadf45e238c46f0 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Thu, 2 Jan 2025 00:49:37 +0800 Subject: [PATCH 20/22] tweaks --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 4953bc9591f5e9..f163fa751a4498 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -79,7 +79,7 @@ This approach ensures maximum compatibility with future migrations. EOAs can rea Reactivation can only be performed by the delegated contract. Since reactivating the private key grants full control over the wallet, including the ability to replace the delegated wallet, wallets must implement this interface with strict security measures. These measures should treat reactivation as the highest level of authority, equivalent to full ownership of the wallet. -The reactivation process is recommended to include a signed authorization from the private key. This signature payload should consist of the chain ID and a random challenge (e.g. a hash value of other signatures) to ensure that: (i) the request is non-replayable, and (ii) the initiators contain the private key owner. +The reactivation process is recommended to include a signed authorization from the private key. This signature payload should consist of the chain ID and a random challenge (e.g. a hash value of other signatures) to ensure that: (i) the request is non-replayable in other chains, and (ii) the the private key owner is also included in the reactivation process. Users should delegate their EOAs only to wallets that have been thoroughly audited and follow best practices for security. From 116eb070391a5813ba655499a71a6b8f9770c814 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 5 Jan 2025 23:21:36 +0800 Subject: [PATCH 21/22] change title name --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index f163fa751a4498..2bbab7e94c5ac2 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,6 +1,6 @@ --- eip: 7851 -title: EOA private key deactivation/reactivation +title: Deactivate/Reactivate of 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 From 38610bdaabc5fe222987f7c738377d05931e89f9 Mon Sep 17 00:00:00 2001 From: colinlyguo Date: Sun, 5 Jan 2025 23:24:28 +0800 Subject: [PATCH 22/22] tweak title --- EIPS/eip-7851.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7851.md b/EIPS/eip-7851.md index 2bbab7e94c5ac2..922f4b4ff265ec 100644 --- a/EIPS/eip-7851.md +++ b/EIPS/eip-7851.md @@ -1,6 +1,6 @@ --- eip: 7851 -title: Deactivate/Reactivate of Delegated EOA's Key +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