Standardizes the introduction of several new algorithms for signing data.
Introduces a new EIP-6404 transaction profile & EIP-2718 transaction type that can modify the signature data of a contained transaction to a different signature algorithm.
Introduces a precompile at address 0x12 for decoding these newly introduced algorithms.
Motivation
As quantum computers become more advanced, several new post-quantum (PQ) algorithms have been designed. These algorithms all have certain issues, 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 currently used secp256k1 curve.
This EIP provides a solution to the diversity in 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.
Unless explicitly noted, integer encoding MUST be in big-endian format.
Parameters
Constant
Value
MAX_ADDITIONAL_INFO
255
GAS_PER_ADDITIONAL_VERIFICATION_BYTE
16
SIGRECOVER_PRECOMPILE_ADDRESS
Bytes20(0x12)
SIGRECOVER_PRECOMPILE_BASE_GAS
3000
ALGORITHMIC_TX_TYPE
Bytes1(0x07)
SECP256K1_SIGNATURE_SIZE is defined in EIP-6404.
ExecutionAddress is defined in EIP-6466.
Algorithmic Transaction (RLP)
This section is only applicable until EIP-6404 and EIP-6466 have been included within a hard fork.
A new EIP-2718 transaction is defined with a structure of:
The signing hash of the payload MUST be calculated with keccak256(rlp([tx_type, tx_data, chain_id, additional_signatures])).
If a signature is being overridden via an additional_signatures entry, the r field MUST be set to keccak256(rlp([algorithm_type, signature_info])) and the v and y_parity values MUST be set to 0x0. This MUST be verified during transaction validation. Each entry MUST NOT be of type secp256k1.
Each additional_signatures entry MUST be used at least once within the transaction and MUST not be duplicated. Non-used or duplicated entries will invalidate the tx. Entries MUST be sorted in ascending order (0x0 -> 0xff…ff) via uint256(keccak256(rlp([algorithm_type, signature_info]))).
The inner transaction MUST still contain the r, s, and y_parity which MUST be set to 0x0.
It MUST also be checked that len(additional_signatures) <= MAX_ADDITIONAL_INFO
Algorithmic Transaction (SSZ)
This section is only applicable once EIP-6404 and EIP-6466 have been included within a hard fork.
TransactionPayload is defined as any type that derives its type from Profile[TransactionPayload].
This EIP introduces a new EIP-6404 transaction profile of SSZ type AlgorithmicTransaction defined below:
classAdditionalSignature(ProgressiveContainer(active_fields=[1,1])):# The selector for which algorithm is to be used.
algorithm_type:uint8,# The signature of the algorithm specified by `algorithm_type`
signature_info:ProgressiveList[byte],classSignableData(ProgressiveContainer(active_fields=[1,1,1])):# `compute_ssz_sig_hash` of the payload field in the main transaction.
payload_hash:Hash32,# The chain id to which the transaction will be sent
chain_id:uint256# The list of additional transaction-level signatures
additional_signatures:List[AdditionalSignature,MAX_ADDITIONAL_INFO]classAlgorithmicTransaction(ProgressiveContainer(active_fields=[1,1,1,1])):# The selector for which algorithm is to be used.
algorithm_type:uint8,# The signature of the algorithm specified by `algorithm_type`
signature_info:ProgressiveList[byte]# The payload of the inner transaction
payload:TransactionPayload,# The chain id to which the transaction will be sent
chain_id:uint256,# The list of additional transaction-level signatures
additional_signatures:List[AdditionalSignature,MAX_ADDITIONAL_INFO]
The signing hash of the payload MUST be calculated using the compute_ssz_sig_hash function below. All SignableData fields MUST be set to the equivalent fields in the transaction body.
If a signature is being overridden via an additional_signatures entry, the Secp256k1ExecutionSignature MUST be set to hash_tree_root(AdditionalSignature) and right-padded with 33 zero bytes. This MUST be verified during transaction validation and each entry MUST NOT be of type secp256k1.
Each additional_signatures entry MUST be used at least once within the transaction and MUST not be duplicated. Non-used or duplicated entries will invalidate the tx. Entries MUST be sorted in ascending order (0x0 -> 0xff…ff) via u256(hash_tree_root(AdditionalSignature)).
identify_transaction_profile modifications
An additional section is added to the start of the identify_transaction_profile helper from EIP-6404:
The field algorithm_type represents the algorithm used to sign the transaction in the payload field.
This EIP does not define algorithms for use with this transaction type apart from secp256k1, which may only be used under specific circumstances.
The transaction signature MUST be verified using the verify function for the algorithm denoted via algorithm_type.
The additional_signatures field only needs to be populated if additional protocol-level signatures are required such as EIP-7702’s authorization_list.
The Algorithmic Transaction MUST generate a receipt of only the inner transaction, not of the AlgorithmicTransaction. 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 inner transaction. If either the transaction or the inner transaction is invalid, the entire transaction is invalid.
Any discrimination of transactions MUST occur only with the inner transaction, e.g. gas prices for block ordering.
Algorithm specification
Further algorithms MUST be specified via a distinct 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 be assumed to be the worst-case scenario and MUST only account for verification costs, not storage nor signing GAS_PENALTY.
A verification function must be present per algorithm. The verification function MUST have the following signature:
The verify function MUST either return an error or return the full public key of the sender.
Specifications MUST include some form of security analysis on the provided algorithm and basic benchmarks justifying gas costs. Additionally, algorithms MUST ensure that variations of signatures cannot be made without the private key.
An example of this specification can be found here.
Deriving address from public keys
The function below MUST be used when deriving an address from a public key:
defpubkey_to_address(public_key:Bytes,algorithm_id:uint8)->ExecutionAddressifalgorithm_id==0xFF:# Compatibility shim to ensure backwards compatibility
returnExecutionAddress(keccak(public_key[1:])[12:])# || is binary concatenation
returnExecutionAddress(keccak(algorithm_id||public_key)[12:])
Algorithm Living EIP
This EIP uses the Algorithms object to signify algorithms that have been included within a hard fork.
A living EIP MAY be created on finalization of this EIP to track currently active algorithms across forks. This living EIP MUST contain a formal definition of the Algorithms object.
Verification
Implementations MUST check that transactions pass the following checks:
The length of the signature_info is less than the algorithm’s defined MAX_SIZE, including additional_signatures entries.
The algorithm_type field points to a known algorithm.
If any checks fail, then the transaction containing them is invalid. If called from the precompile, the precompile MUST return bytes20(0x0).
Transactions MUST also follow all rules outlined in the Transaction specification RLP or Transaction specification SSZ sections.
Gas calculation
All transactions that use more resources than the secp256k1 curve suffer an additional penalty which MUST be calculated
as specified in the calculate_penalty function.
The penalty MUST be added to the 21000 base gas of each transaction before the transaction is processed. A penalty MUST also be added for every instance of an additional_signatures object. If the payload’s gas_limit is less than 21000 + penalty then the transaction MUST be invalidated.
This transaction also MUST inherit the intrinsics of the wrapped transaction’s fee structure (e.g. a wrapped EIP-1559 payload would behave as an EIP-1559 transaction).
secp256k1 algorithm
Field
Value
ALG_TYPE
0xFF
MAX_SIZE
SECP256K1_SIGNATURE_SIZE
GAS_PENALTY
0
secp256k1_validate and SECP256K1_SIGNATURE_SIZE are defined in EIP-6404.
defverify(signature_info:bytes,payload_hash:Hash32)->Bytesassertlen(signature_info)==SECP256K1_SIGNATURE_SIZEsecp256k1_validate(signature_info)# This code is from [EIP-6404](/EIPS/eip-6404)'s `secp256k1_recover_signer` function without
# the final hashing stage.
ecdsa=ECDSA()recover_sig=ecdsa.ecdsa_recoverable_deserialize(signature_info[0:64],signature_info[64])public_key=PublicKey(ecdsa.ecdsa_recover(payload_hash,recover_sig,raw=True))uncompressed=public_key.serialize(compressed=False)returnuncompressed
This algorithm MUST ONLY be used if the transaction’s signature is secp256k1 but the additional_signatures field contains one or more entries. It MUST NOT be present if the transaction does not contain additional signatures. secp256k1 MUST NOT be present in any additional_signatures entry.
sigrecover precompile
This EIP also introduces a new precompile located at SIGRECOVER_PRECOMPILE_ADDRESS.
This precompile MUST cost SIGRECOVER_PRECOMPILE_BASE_GAS when called regardless of validity. This cost MUST be aggregated with calculate_penalty(signature_info, algorithm) and charged before the sigrecover precompile executes.
The precompile MUST output the 20-byte address of the signer provided. Callers MUST assume all zero bytes as a failure.
The precompile logic contains the following logic:
defsigrecover_precompile(input:Bytes)->Bytes:# Recover signature, hash and type
assert(len(input)>=33)hash:Hash32=input[:32]algorithm_type:uint8=input[32]signature:Bytes=input[33:]# Ensure the algorithm exists
ifalgorithm_typenotinAlgorithms:returnExecutionAddress(0x0)alg=Algorithms[algorithm_type]# Check sig is smaller than the maximum
iflen(signature)>alg.MAX_SIZE:returnExecutionAddress(0x0)# Run verify function
try:pubkey=alg.verify(signature,hash)returnpubkey_to_address(pubkey,algorithm_type)except:returnExecutionAddress(0x0)
Rationale
Opaque signature_info type
As each algorithm has unique properties, e.g. signature recovery and key sizes, the object needs to hold every permutation of every possible signature and additional recovery information. A bytearray of a dynamic size would be able to achieve this goal. However, this leads to a DoS vector which the Gas penalties section solves.
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 data type and must persist indefinitely to ensure later verification.
Not specifying account key-sharing / migration
Allowing a single account to share multiple keys creates a security risk as it reduces the security of all addresses 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 would not be bound by the lowest common denominator, it creates an excessively large burden on client and tooling developers.
New precompile over modifying the ecrecover precompile
Initially, modifying the ecrecover precompile seemed prudent over creating a new precompile. However, after an initial attempt, it proved too hacky for production use.
This EIP allows for EIP‑4337Bundlers to settle UserOperations on-chain using a different algorithm. sigrecover also complements Account Abstraction well by reducing on-chain verification costs via sigrecover.
Backwards Compatibility
Non-EIP-7932 transactions will still be processed as the default secp256k1 curve. Therefore, there would be no backwards compatibility issues in processing other transactions.
Test Cases
Security Considerations
Allowing more ways to potentially sign 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 discussion 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 algorithm’s decoding logic could be specified or implemented incorrectly, which could lead to, in the best case, invalid blocks or, at worst, enabling anyone to sign a fraudulent transaction for any account. This security consideration is delegated to the algorithm’s specification, and so care must be taken when writing these specifications to avoid critical security flaws.