-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Add EIP: Algorithmic Transaction Wrapper #9633
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
eth-bot
merged 31 commits into
ethereum:master
from
SirSpudlington:Add-eip-multi-algorithm-pki
May 13, 2025
Merged
Changes from all commits
Commits
Show all changes
31 commits
Select commit
Hold shift + click to select a range
d0a2c78
Add EIP: Multi-algorithm signing support
SirSpudlington 1bfc3f3
Fix grammar
SirSpudlington 4c275e6
Refactor title and desc
SirSpudlington 88c7e85
Fix typos and reformat
SirSpudlington 45f8cdb
Fix typos and reformat
SirSpudlington 26cfed7
Implement @jochem-brouwer 's feedback
SirSpudlington d6e2650
Remove default and add example template
SirSpudlington 8ac2404
Add test case message
SirSpudlington 514bf6a
Add discussions-to link
SirSpudlington 6aa44b6
Add future proofing information
SirSpudlington 8e4e2f5
Add additional gas message
SirSpudlington f0cbf89
Specify how this transaction interfaces with existing fee structures
SirSpudlington f9fc06d
Apply suggestions from code review
SirSpudlington 7b37cbe
Update draft-eip-multi-algorithm-pki.md
SamWilsn e9991dd
Rename draft-eip-multi-algorithm-pki.md to eip-7932.md
SamWilsn 764ff3e
Commit to merge
SirSpudlington 723624d
Merge branch 'Add-eip-multi-algorithm-pki' of https://github.com/JKin…
SirSpudlington 78ccb3a
Modifications to `ecrecover` and `additional_info` for other signatures
SirSpudlington 757b779
Fix linting issues
SirSpudlington c27a639
Add additional condition
SirSpudlington 05d9d8c
Add `NULL algorithm` for edge cases such as eip-7702 tx's where the `…
SirSpudlington ccc94a5
Retrigger checks
SirSpudlington 0672749
Fix linking issues
SirSpudlington 43733e8
Small tweaks for `chain_id` values.
SirSpudlington a44023f
Add new precompile and remove redundant boolean
SirSpudlington 53eb0b7
Small tweaks
SirSpudlington e3d3a5c
Improve clarity & denote edge cases
SirSpudlington 11ff0dd
Linting & grammar
SirSpudlington 9771654
More modifications and clarifications based on @g11tech feedback
SirSpudlington 63da070
Fix typos
SirSpudlington cac067e
Update to new Magicians link
SirSpudlington File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
--- | ||
eip: 7932 | ||
title: Algorithmic Transaction Wrapper | ||
description: Introduces a new transaction type wrapping transactions in alternative signature algorithms | ||
author: James Kempton (@SirSpudlington) | ||
discussions-to: https://ethereum-magicians.org/t/eip-7932-algorithmic-transaction-wrapper/23514 | ||
status: Draft | ||
type: Standards Track | ||
category: Core | ||
created: 2025-04-12 | ||
requires: 155, 1559, 2718, 2930, 4844, 7702 | ||
--- | ||
|
||
## Abstract | ||
|
||
This EIP introduces a new [EIP-2718](./eip-2718.md) typed transaction that wraps (contains) another transaction, this EIP nullifies the default signature parameters and appends signature data to the front of the transaction with a selector, this effectively wraps a transaction and swaps out signature data for alternative algorithms and data. It also creates a new precompile to be able to decode these additional signature algorithms. | ||
|
||
## Motivation | ||
|
||
As quantum computers are getting more advanced, several new post-quantum (PQ) algorithms have been designed. These algorithms all contain drawbacks such as large key sizes (>1KiB), large signature sizes or long verification times. These issues make them more expensive to compute and store than the current secp256k1 curve in use (as of 2025-04-12). | ||
|
||
This EIP provides a future-proof solution to these algorithms by adding a standardized way to represent alternative algorithms within a transaction. | ||
|
||
## 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. | ||
|
||
Note: The "verification function" referred to by this EIP is the one specified by additional EIP & their signature types. When calling explicitly the "verification function" implementations MUST NOT call the precompile due to differing gas costs. | ||
|
||
### Parameters | ||
|
||
| Constant | Value | | ||
| - | - | | ||
| `ALG_TX_TYPE` | `Bytes1(0x07)` | | ||
| `GAS_PER_ADDITIONAL_VERIFICATION_BYTE`| `16` | | ||
| `SIGRECOVER_PRECOMPILE_ADDRESS`| `Bytes20(0x12)` <!-- TODO: Ensure that this is not already in use --> | | ||
| `SIGRECOVER_PRECOMPILE_BASE_GAS` | `3000` | | ||
|
||
### Algorithmic Transaction | ||
|
||
This EIP introduces a new [EIP-2718](./eip-2718.md) transaction with a `TransactionType` of `ALG_TX_TYPE` and a `TransactionPayload` of the RLP serialization of the `AlgTransactionPayloadBody` defined below: | ||
|
||
`[alg_type, signature_info, parent, additional_info]` | ||
|
||
The field `alg_type` is an unsigned 8-bit integer (uint8) that represents the algorithm used to sign the transaction in the `parent` field. This EIP does not define algorithms for use with this transaction type; however, it does specify a NULL algorithm (0xFF) which MUST trigger implementations to verify transaction fields. | ||
|
||
The `signature_info` field contains information required to verify the signature of the transaction in the `parent` field. This is a byte-array of arbitrary length, which would be passed to the verification function. | ||
|
||
The `parent` field contains another serialized [EIP-2718](./eip-2718.md) Typed Transaction Envelope (i.e. the `parent` field type is `bytes`), which MUST be able to contain every possible `TransactionType`, including legacy transactions with a `TransactionType` of `> 0x7f`, but the only exception to this rule is the `Algorithmic Transaction` itself, which MUST NOT be placed within itself. | ||
|
||
These `parent` transactions all contain `y_parity`, `r`, `s` values, which MUST be set to `Bytes0()` if wrapped in a `AlgTransactionPayloadBody`, there are two exceptions to this rule however: | ||
|
||
- If the `alg_type` is the [NULL algorithm](#null-algorithm), the signature field MUST be left unchanged. | ||
- If the transaction type is that of a legacy transaction, where `y_parity` MUST be equal to the chain ID value, the transactions signing data MUST also be calculated the same way as [EIP-155](./eip-155.md) specifies | ||
|
||
All other transaction values MUST be unchanged from their original values. | ||
|
||
The `additional_info` field only needs to be populated if additional protocol level signatures are required such as [EIP-7702](./eip-7702.md)'s `authorization_list`. This field MUST contain the RLP serialization of a list of the signatures `[alg_type, signature_info]` and MUST be repeated for every NON-NULL signature inside the transaction. The order of which MUST be the same as the signatures appear in the `parent` tx. This signature MUST also be checked to ensure that `alg_type` is known and is not the NULL algorithm (0xFF), `len(signature_info) <= alg.MAX_SIZE` and then MUST also be verified using the `verify` function for the specific algorithm. | ||
SirSpudlington marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
If new transaction types are specified they MUST NOT attempt to build on this EIP but instead MUST include the `y_parity`, `r`, `s` values, this will prevent backwards compatibility issues and ensure that any transaction other than EIP-7932 txs can be safely assumed to be secp256k1. | ||
|
||
The Algorithmic Transaction MUST NOT generate a transaction receipt with a `TransactionType` of `ALG_TX_TYPE`, it MUST emit the receipt of the transaction it is wrapping (the tx in the `parent` field). Implementations MUST not be able to differentiate between an unwrapped and wrapped transaction by receipts alone. | ||
|
||
When clients receive an Algorithmic Transaction via gossip or RPC, they MUST validate both the Algorithmic Transaction and the transaction in the `parent` field, any ordering (e.g. based on gas price) MUST be done on the transaction in the `parent` field. If either transaction is invalid they MUST NOT propagate the transaction to peers. | ||
|
||
#### Example transaction | ||
|
||
This is an example wrapped [EIP-1559](./eip-1559.md) transaction where `alg_type == 0x5` | ||
|
||
`ALG_TX_TYPE || rlp([0x5, b"signing-data", 0x02 || rlp([0x1, 0x1, 0x1, 0x1, 21000, 0x0, 0x1, 0x, [], 0x0, 0x0, 0x0])])` | ||
|
||
The `parent` field serializes to `0x02ce01010101825208800180c0808080`, therefore the above transaction serializes to `0x07df058c7369676e696e672d646174619002ce01010101825208800180c0808080` | ||
|
||
### Algorithm specification | ||
|
||
Further algorithms MUST be specified via an additional EIP. | ||
|
||
Each type of algorithm MUST specify the following fields: | ||
| Field Name | Description | | ||
|-|-| | ||
|`ALG_TYPE`| The uint8 of the algorithm unique ID | | ||
|`MAX_SIZE`| The maximum size of `signature_info` field in a transaction | | ||
|`GAS_PENALTY`| The additional gas penalty from verification of the signature | | ||
|
||
The `GAS_PENALTY` field MUST only account for verification costs, not storage nor signing. | ||
|
||
New algorithms MUST also specify how to recover and verify a valid address (`bytes20`) from the `signature_info` field inside the transaction, the verification function MUST follow the following signature: | ||
|
||
`def verify(signature_info: bytes, parent_hash: bytes32) -> bytes20` | ||
|
||
The verify function MUST return `0x0` if there was an error recovering a valid address from the signature, otherwise the function MUST return the address of the signer. | ||
|
||
Specifications MUST also justify why their `GAS_PENALTY` is high enough to not cause a DOS vector, and MUST include some form of security analysis on the algorithm and that it will not cause potential security issues. | ||
|
||
An example of this specification can be found [here](../assets/eip-7932/template-eip.md). | ||
|
||
This EIP uses the `Algorithms` object to signify final and active algorithms. These are selected via the EIP process and hard-fork inclusion. | ||
|
||
### Verification | ||
|
||
Implementations MUST consider transactions invalid where `len(tx.signature_info) > alg.MAX_SIZE`, this also applies for objects inside `additional_info`. | ||
|
||
The following checks MUST always be made against any type of `signature_info` and `alg_type`. If the result of these checks do not pass then one of the following apply: | ||
|
||
1. The transaction is invalid | ||
2. If called from the precompile, the precompile MUST return 0x0 as the address. | ||
|
||
```python | ||
assert(Algorithms[alg_type] != None) # `Algorithms` is a dictionary containing every defined algorithm | ||
assert(len(signature_info) <= alg.MAX_SIZE) | ||
``` | ||
|
||
The validity/processing of the transaction should be processed similarly to the following function: | ||
|
||
```python | ||
def process_transaction(tx: Transaction, from_address: bytes20 = None, start_gas = 21000, additional_info: List[Callable[[bytes32], bytes20]]): | ||
match tx: | ||
# Verification for other transactions, if `from_address != None` | ||
# then the verifier MUST NOT attempt to validate the `y_parity`, `r`, `s` | ||
# parameters. Additionally, the verification function MUST start the initial gas value | ||
# at `start_gas`. Every further signature where all parameters are null MUST instead use | ||
# `additional_info` field corresponding to to which they appeared (i.e. the first signature to be null is at additional_info[0]). | ||
# If the transaction is a legacy transaction and `from_address != None`, implementations | ||
# MUST ensure that `y_parity == chainid`. | ||
... | ||
|
||
ALG_TX: | ||
assert(from_address == None) # Ensure no double-wrapping | ||
assert(Algorithms[alg_type] != None) | ||
|
||
alg = Algorithms[alg_type] | ||
assert(len(signature_info) <= alg.MAX_SIZE) | ||
|
||
from_address = None | ||
if alg_type != 0xFF: # NULL TX | ||
valid, from_address = alg.verify(tx.signature_info, calculate_signing_hash(wrapped), chain_id) # calculate_signing_hash is defined within the wrapped transaction's EIP. | ||
assert(valid) | ||
|
||
# This is only an example, a method like this should not be used in production, instead the signature info should be passed | ||
# into the processing function. | ||
|
||
additional_info_verifiers = [] | ||
|
||
for additional_info in tx[3:]: | ||
additional_info_verifiers.append(lambda sig_hash: Algorithms[additional_info.alg_type].verify(additional_info.signature_info, calculate_signing_hash(sig_hash))) | ||
|
||
process_transaction(tx.parent, from_address, 21000 + calculate_penalty(tx.signature_info, alg)) | ||
``` | ||
|
||
### Gas calculation | ||
|
||
All transactions that use more resources than the secp256k1 curve suffer an additional penalty. This penalty MUST be calculated as follows: | ||
|
||
```python | ||
def calculate_penalty(signing_data: bytes, algorithm: int) -> int: | ||
gas_penalty_base = max(len(signing_data) - 65, 0) * GAS_PER_ADDITIONAL_VERIFICATION_BYTE | ||
total_gas_penalty = gas_penalty_base + ALGORITHMS[algorithm].GAS_PENALTY | ||
return total_gas_penalty | ||
``` | ||
|
||
The penalty MUST be added onto the `21000` base gas of each transaction BEFORE the transaction is processed. If the wrapped tx's `gas_limit` is less then `21000 + calculate_penalty(signing_data, algorithm)` than the transaction MUST be considered invalid and MUST NOT be included within blocks. This transaction also MUST inherit the intrinsics of the wrapped tx's fee structure (e.g. a wrapped EIP-1559 tx would behave as a EIP-1559 tx). | ||
|
||
The penalty MUST be applied to every instance of an `additional_info` object, which then MUST be added onto the transaction's initial gas. | ||
|
||
### `sigrecover` precompile | ||
|
||
This EIP also introduces a new precompile located at `SIGRECOVER_PRECOMPILE_ADDRESS`. | ||
|
||
This precompile MUST NOT be called when verifying transaction parameters, instead this call should be verified as specified above. | ||
|
||
This precompile MUST cost `SIGRECOVER_PRECOMPILE_BASE_GAS` when calling even if algorithm does not exist, this price MUST be aggregated with algorithm specific `GAS_PENALTY` and the output of `calculate_penalty` for the signature data, this penalty is charged once the `sigrecover` precompile executes. | ||
|
||
The precompile logic executes the following logic: | ||
|
||
```python | ||
|
||
def sigrecover_precompile(input: Bytes) -> Bytes: | ||
# Recover signature length and type | ||
assert(len(input) >= 64) | ||
hash = input[:32] | ||
alg_type = input[32] | ||
sig_length = int.from_bytes(input[33:64], "little") | ||
|
||
# Ensure the algorithm exists and signature is correct size | ||
if alg_type not in Algorithms: | ||
return bytes20(0x0) | ||
|
||
alg = Algorithms[alg_type] | ||
|
||
if sig_length > alg.MAX_SIZE: | ||
return bytes20(0x0) | ||
|
||
# Run verify function | ||
return alg.verify(input[64:64 + sig_length], hash) | ||
|
||
``` | ||
|
||
### NULL algorithm | ||
|
||
The NULL algorithm (0xFF) MUST be present if the signature parameters inside the wrapped transaction are still valid for `secp256k1`, but there are more parameters in the transaction such as [EIP-7702](./eip-7702.md)'s `authorization_list` that require swapping out for different algorithms. | ||
|
||
The `signature_info` field MUST be zero-sized if the NULL algorithm is not present. | ||
|
||
The NULL algorithm MUST NOT be present if the transaction does not contain additional signatures. | ||
|
||
## Rationale | ||
|
||
### Setting `y_parity`, `r`, `s` values to zero rather than removing them | ||
|
||
Keeping the `y_parity`, `r`, `s` values inside the transactions keeps the previous parsing, verification and processing logic the same and allows for minimal modification to the other specifications while still preventing excessive space usage. | ||
|
||
### Opaque `signature_info` type | ||
|
||
As each algorithm has unique properties, i.e. signature recovery and key sizes, a object is needed to hold every permutation of every possible key and signature. A bytearray of dynamic size would be able to achieve this goal, this does lead to a DoS vector which the [Gas penalties](#gas-penalties) section solves along with the `MAX_SIZE` parameter. | ||
|
||
### Gas penalties | ||
|
||
Having multiple different algorithms results in multiple different signature sizes, and verification costs. Hence, every signature algorithm that is more expensive than the default ECDSA secp256k1 curve, incurs an additional gas penalty, this is to discourage the use of overly expensive algorithms for no specific reason. | ||
|
||
The `GAS_PER_ADDITIONAL_VERIFICATION_BYTE` value being `16` was taken from the calldata cost of a transaction, as it is a similar datatype and must persist indefinitely to ensure later verification. | ||
|
||
### Not adding the `secp256k1` curve | ||
|
||
Having a type for `secp256k1` allows for several iterations of the same object to be present across the network. Additionally the only purpose for this curve would be for prototyping and testing with client teams, as the resultant receipt, logs and state change would be the same as a non-wrapped transaction. | ||
|
||
### Not specifying account key-sharing / migration | ||
|
||
Allowing a single account to share multiple keys creates a security risk as it reduces the security to the weakest algorithm. This is also out of scope for this EIP and could be implemented via a future EIP. | ||
|
||
### Keeping a similar address rather than introducing a new address format | ||
|
||
While adding a new address format for every new algorithm would ensure that collisions never happen and that security is not bound by the lowest common denominator, the amount of changes that would have to be made and backwards compatibility issues would be too vast to warrant this. | ||
|
||
### New precompile over modifying the `ecrecover` precompile | ||
|
||
Initially, modifying the `ecrecover` precompile was going to be selected over creating a new precompile, however, this was ruled out after it took too much complexity to implement or may break backwards compatibility. | ||
|
||
### Hard fork over [EIP‑4337](./eip-4337.md) Account Abstraction | ||
|
||
This EIP allows for [EIP‑4337](./eip-4337.md) `Bundler`s to settle `UserOperation`s onchain using a different algorithm which in the future may be the only option if post-quantum issues cause the secp256k1 curve to be phased out. | ||
|
||
## Backwards Compatibility | ||
|
||
Non-EIP-7932 transactions will still be included within blocks and will be treated as the default secp256k1 curve. Therefore there would be no backwards compatibility issues will processing other transactions. However, as a new [EIP-2718](./eip-2718.md) transaction has been added non-upgraded clients would not be able to process these transactions nor blocks that include these transactions. | ||
|
||
## Test Cases | ||
|
||
These test cases do not involve processing other types of transactions. Only the wrapping, unwrapping and verification of these transactions without interfacing with the `parent` tx held inside the main tx. | ||
|
||
All the following test cases use the parameters from the example eip specified in the [Algorithm Specification Section](../assets/eip-7932/template-eip.md) listed above. | ||
|
||
<!-- TODO, must be done before EIP enter review stage. --> | ||
Check warning on line 252 in EIPS/eip-7932.md
|
||
|
||
## Security Considerations | ||
|
||
Allowing more ways to potentially create transactions for a single account may decrease overall security for that specific account, however this is partially mitigated by the increase in processing power required to trial all algorithms. Even still, adding additional algorithms may need further discussing to ensure that the security of the network would not be compromised. | ||
|
||
Having `signature_info` be of no concrete type creates a chance that an algorithms logic could be specified or implemented incorrectly, which could lead to, in the best case, invalid blocks, or at worst, the ability for anyone to sign a fraudulent transaction for any account. This security consideration is delegated to the algorithms specification, therefore care must be taken when writing these algorithm specifications to avoid critical security flaws. | ||
|
||
<!-- TODO: Further security considerations need discussion. --> | ||
|
||
## Copyright | ||
|
||
Copyright and related rights waived via [CC0](../LICENSE.md). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
--- | ||
title: Example EIP to add secp256k1 curve as an algorithmic type | ||
description: Example EIP to add secp256k1 curve as an algorithmic type | ||
SirSpudlington marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Author: ExampleAuthor | ||
discussions-to: fakeurl | ||
status: Draft | ||
type: Standards Track | ||
category: Core | ||
created: 2025-04-12 | ||
requires: 7932 | ||
--- | ||
|
||
## Abstract | ||
This example EIP adds secp256k1 curve as an algorithmic type. | ||
|
||
## Motivation | ||
secp256k1 is the commonly used curve, therefore it should be added. | ||
|
||
## Specification | ||
|
||
This EIP defines a new [EIP-7932](../../EIPS/eip-7932.md) algorithmic type with the following parameters. | ||
|
||
| Constant | Value | | ||
| - | - | | ||
| `ALG_TYPE` | `Bytes1(0x0)` | | ||
| `GAS_PENALTY`| `0` | | ||
| `MAX_SIZE` | `65` | | ||
|
||
```python | ||
def verify(signature_info: bytes, parent_hash: bytes32) -> bytes20: | ||
assert(len(signature_info) == 96) | ||
r, s, v = signature_info[0:32], signature_info[32:64], signature_info[64:] | ||
|
||
# This assumes `ecrecover` is identical to the `ecrecover` function in solidity. | ||
signer = ecrecover(parent_hash, v, r, s) | ||
|
||
return signer | ||
``` | ||
|
||
## Rationale | ||
secp256k1 is the commonly used curve, therefore it should be added. | ||
|
||
## Backwards Compatibility | ||
No backward compatibility issues found. | ||
|
||
## Security Considerations | ||
Needs discussion. | ||
|
||
## Copyright | ||
Copyright and related rights waived via [CC0](../../LICENSE.md). |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.