EIP.tools

EIP.tools

⚠️ DraftStandards Track: Core

EIP-8215: Hash-Committed Account

Account type where address commits to a Merkle root of spending conditions, not a public key

Authors
Created2026-04-02
Discussion Linkhttps://ethereum-magicians.org/t/discussion-hash-committed-account-hca-quantum-safe-address-derivation-for-ethereum/28094
Requires

Markdown

https://raw.githubusercontent.com/ethereum/EIPs/re...
Pull Request#11480PR open

EIP-GPT summary

Contents
AbstractMotivationThe address derivation gapThe quantum threatWhy protocol-level, not application-levelRelationship to existing proposalsSpecificationConstantsAccount state changesTagged hash constructionLeaf structureMerkle tree constructionAddress derivationTransaction typeWitness structureVerification procedureLeaf execution contextGas schedulePerformance impactauthRoot rotationTransaction signing hashRationaleWhy tagged hash with BIP-340 conventionWhy single-hash address derivationWhy 20-byte addressesWhy EVM bytecode leavesWhy depth-32 maximumWhy CALL is banned staticallyWhy not EIP-8141 + CREATE2Why not ZK-STARK proof of key knowledgeWhy not hybrid PQC+EC address derivationWhy complement, not compete with EIP-8141Backwards CompatibilityExisting accountsmsg.sender behaviorHard fork requirementState tree interactionTest CasesTagged hash domain separationAddress derivationMerkle proof verificationSigning hash replay protectionSigning hash calldata sensitivityReference ImplementationSecurity ConsiderationsFormal security reductionAddress collision analysisReplay protectionLeaf-branch domain separationDenial of service via leaf script executionTiming side-channel resistancePost-quantum signature scheme independenceUser misconfiguration risksWallet safety requirementsCopyright

Abstract

This EIP introduces Hash-Committed Account (HCA), a new Ethereum account type where the address is derived from a Merkle root of spending conditions rather than from a public key. The address derivation formula address = tagged_hash("HCAAddr", auth_root)[12:] eliminates public keys from the commitment chain entirely. When spending, the transaction witness contains a leaf script, a Merkle proof, and signature data. Verification proceeds without ecrecover — the sender address is an explicit transaction field. HCA is signature-scheme agnostic: ECDSA leaves work today, post-quantum leaves work when standardized via EIP-7932.

Motivation

The address derivation gap

Ethereum's post-quantum roadmap includes strong proposals for signature validation (EIP-8141), algorithm standardization (EIP-7932), and state storage (EIP-7864). Each changes how transactions are signed, which algorithms are available, or how state is stored. None changes how the address is derived.

After all three proposals are fully deployed, the address derivation formula for a new externally owned account remains:

address = keccak256(secp256k1_public_key)[12:]

The public key is permanently coupled to account identity. The first transaction from any EOA exposes this key on-chain via ecrecover. Once exposed, the key becomes a static target for a Cryptographically Relevant Quantum Computer (CRQC) running Shor's algorithm.

This is a commitment problem, not a signature problem. The signature layer can be upgraded via account abstraction. The commitment layer — what the address itself is derived from — has no upgrade path.

The quantum threat

On March 30, 2026, Google Quantum AI published research demonstrating that ECDLP-256 can be broken with fewer than 500,000 physical qubits in a few minutes — a 20-fold reduction from previous estimates. Google set a 2029 internal migration deadline. The paper specifically recommends "refraining from exposing or reusing vulnerable wallet addresses."

Government mandates establish hard deadlines:

  • CNSA 2.0 (NSA, September 2022): software and networking equipment MUST migrate to post-quantum cryptography by 2030, with full upgrade by 2033.
  • NIST IR 8547 (November 2024): ECDSA with P-256 deprecated after 2030, disallowed after 2035 for all federal systems. Target: widespread PQC adoption by 2035.

The Ethereum Foundation's Post-Quantum portal states: "migrating a decentralized, global protocol takes years of coordination, engineering, and formal verification. The work must begin well before the threat arrives."

HCA's migration across wallets, DeFi protocols, exchanges, L2 sequencers, and hardware wallets will itself require multiple years. Initiating this work in 2026 is consistent with a 2029-2033 migration window.

Why protocol-level, not application-level

A smart contract wallet deployed via CREATE2 can achieve functionally similar behavior — committing an address to an authentication policy via the CREATE2 salt without embedding a public key. This raises the question of whether HCA should exist as a native account type or as an application-layer pattern.

A native account type is justified for three reasons:

  1. Gas efficiency. A simple HCA spend (single-leaf tree) costs approximately 450 gas for Merkle verification. A CREATE2 smart contract wallet requires deployment gas (~32,000), proxy storage, and delegatecall overhead per transaction.
  2. Simplicity. HCA account creation requires no factory contract, no proxy pattern, no deployment transaction. A user constructs a Merkle tree, derives an address, and receives funds.
  3. Protocol clarity. A first-class account primitive signals that quantum-safe address derivation is a protocol-level concern, not an afterthought delegated to application developers.

The precedent exists: BIP-360 introduces Pay-to-Merkle-Root (P2MR) as a native Bitcoin output type alongside Taproot, despite Taproot's script-path spends achieving functionally similar results. HCA is to EIP-8141 what P2MR is to Taproot.

Relationship to existing proposals

HCA is not an account abstraction proposal. Account abstraction (EIP-8141, ERC-4337, EIP-7702) changes how transactions are validated — it decouples signature verification from ECDSA. HCA changes what the address itself commits to — it decouples address derivation from public keys. These operate at different layers:

  • EIP-7864: quantum-safe state storage (how data is stored)
  • EIP-7932: quantum-safe algorithm registry (which algorithms exist)
  • EIP-8141: quantum-safe transaction validation (how you sign)
  • HCA: quantum-safe address derivation (what your address commits to)

Together, these four proposals cover every layer of the Ethereum account lifecycle from address creation to transaction execution to state persistence.

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.

Constants

ConstantValueDescription
HCA_TX_TYPE0x05EIP-2718 transaction type identifier
MAX_TREE_DEPTH32Maximum Merkle tree depth (supports 2^32 leaves)
MAX_LEAF_GAS100000Maximum gas for leaf script execution
MAX_LEAF_SCRIPT_SIZE24576Maximum leaf script size in bytes (24 KB)
MAX_WITNESS_SIZE65536Maximum witness size in bytes (64 KB)
MERKLE_BASE_GAS200Base gas for Merkle proof verification
MERKLE_GAS_PER_LEVEL80Gas per Merkle tree level

Account state changes

An HCA account introduces one new 32-byte field, authRoot, to the account state:

FieldSizeDescription
nonce8 bytesTransaction counter
balance32 bytesETH balance in wei
authRoot32 bytesMerkle root of the authentication policy tree
codeHash32 byteskeccak256("") (HCA accounts are not contracts)

The authRoot field MUST be set when the account is first created and MUST NOT be modified except via the rotation mechanism defined in this specification.

An account is identified as an HCA account if authRoot is nonzero and codeHash equals the empty code hash.

Tagged hash construction

HCA uses domain-separated hashing following the BIP-340 tagged hash convention adapted for Keccak-256:

tagged_hash(tag, data) = keccak256(SHA256(tag) || SHA256(tag) || data)

The double SHA256(tag) prefix provides provable domain separation between different operations. Five domain tags are defined:

TagConstantUsage
"HCAAddr"ADDRAddress derivation
"HCALeaf"LEAFLeaf hash computation
"HCABranch"BRANCHBranch node hash computation
"HCAWitness"WITNESSTransaction signing hash
"HCARotate"ROTATEauthRoot rotation authorization

Different tags MUST produce different outputs for identical inputs. This property ensures that a leaf hash can never equal a branch hash, an address hash can never collide with a witness hash, and cross-protocol collision attacks are prevented.

Leaf structure

A leaf represents a single spending condition. Each leaf is a byte sequence with a 1-byte version prefix:

leaf = version_byte || script_bytecode
VersionMeaning
0x00Reserved (MUST be rejected as invalid)
0x01HCA v1 — EVM bytecode spending condition
0x02EIP-7932 algorithm registry dispatch
0x03–0x0FReserved for future standardized PQ schemes
0x10–0xFFReserved for experimental and application-specific use

The leaf hash is computed as:

leaf_hash = tagged_hash("HCALeaf", version_byte || script_bytecode)

Leaf scripts MUST NOT exceed MAX_LEAF_SCRIPT_SIZE bytes. Leaf scripts MUST NOT be empty. Leaf version 0x00 MUST be rejected during account creation and transaction verification.

Merkle tree construction

The authentication policy tree is a standard binary Merkle tree:

  1. Compute leaf_hash for each leaf.
  2. If the number of leaves is not a power of two, pad by duplicating the last leaf hash until it is.
  3. Build the tree bottom-up. Each branch node is computed as:
branch_hash = tagged_hash("HCABranch", left_child || right_child)
  1. The root of the tree is the auth_root:
auth_root = merkle_root([leaf_hash_0, leaf_hash_1, ..., leaf_hash_n])

The tree depth MUST NOT exceed MAX_TREE_DEPTH. Leaf ordering is significant — reordering leaves produces a different auth_root. All leaves within a single tree MUST have distinct leaf hashes — duplicate leaves MUST be rejected during tree construction.

Domain separation between leaf and branch hashing (different tagged hash tags) makes leaf-branch confusion attacks impossible by construction.

Address derivation

The HCA address is derived from the auth_root with no public key in the derivation chain:

address = tagged_hash("HCAAddr", auth_root)[12:]

The last 20 bytes of the tagged hash output are taken, matching Ethereum's existing 20-byte address format for full EVM compatibility. The tagged_hash("HCAAddr", ...) prefix provides domain separation from EOA addresses (keccak256(pubkey)[12:]), CREATE addresses (keccak256(rlp([sender, nonce]))[12:]), and CREATE2 addresses (keccak256(0xff || factory || salt || init_code_hash)[12:]).

Since tagged_hash already applies keccak256 internally, the address derivation involves a single keccak256 computation with domain-separated input. No additional outer hash is applied.

Transaction type

HCA transactions use EIP-2718 typed transaction envelope with type HCA_TX_TYPE:

HCA_TX_TYPE || RLP([
    chain_id,
    nonce,
    sender,                    // 20 bytes — explicit HCA address
    to,                        // 20 bytes — recipient
    value,                     // uint256 — wei to transfer
    data,                      // bytes — calldata
    max_priority_fee_per_gas,  // uint256
    max_fee_per_gas,           // uint256
    access_list,               // []
    leaf_version,              // uint8
    leaf_script,               // bytes — spending condition bytecode
    leaf_index,                // uint32 — index in Merkle tree
    merkle_proof,              // bytes32[] — sibling hashes
    witness_data               // bytes — signature or proof data
])

The sender field is explicit. There is no ECDSA signature recovery. The sender's identity is verified through the witness (Merkle proof + leaf script execution), not through ecrecover.

Witness structure

The witness consists of four components included in the transaction:

FieldTypeDescription
leaf_versionuint8Version byte of the spending condition
leaf_scriptbytesSpending condition bytecode
merkle_proofbytes32[]Sibling hashes from leaf to root
witness_databytesSignature or other data the leaf script requires

The total witness size MUST NOT exceed MAX_WITNESS_SIZE.

Verification procedure

Verification of an HCA transaction proceeds in three sequential steps. If any step fails, the transaction MUST be rejected.

Step 1 — Merkle proof verification:

  1. Compute leaf_hash = tagged_hash("HCALeaf", leaf_version || leaf_script).
  2. Starting from leaf_hash, walk the merkle_proof siblings upward, computing branch hashes at each level: current = tagged_hash("HCABranch", left || right) where the ordering is determined by the leaf index bit at each level.
  3. The final computed root MUST equal the authRoot stored in the sender's account state.
  4. Merkle proof verification MUST use constant-time comparison for the final root equality check to prevent timing side-channel leaks.

Step 2 — Leaf script execution:

  1. Execute the leaf script in a restricted EVM context (see Leaf Execution Context below).
  2. The leaf script receives witness_data and the transaction signing hash as inputs.
  3. The leaf script MUST return 1 (valid). Any other return value, a revert, or an out-of-gas condition MUST be treated as invalid.

Step 3 — Transaction execution:

If both verification steps pass, the transaction executes normally. The HCA address is set as msg.sender. From the perspective of called contracts, the transaction is indistinguishable from one sent by an EOA.

Leaf execution context

Leaf scripts execute in a restricted EVM context to prevent denial-of-service attacks and ensure verification is pure:

Gas limit: Leaf execution MUST NOT exceed MAX_LEAF_GAS (100,000 gas). Gas is metered per-opcode according to the EVM Berlin gas schedule.

Banned opcodes: The following opcodes MUST NOT appear in leaf scripts. Nodes MUST reject transactions containing leaf scripts with banned opcodes.

OpcodeReason
CREATE, CREATE2No contract deployment during verification
SSTORENo state mutation
SELFDESTRUCTNo account destruction
CALLNo external calls (value transfer is a runtime property; banned statically for safety)
DELEGATECALLNo arbitrary code execution
LOG0–LOG4No event emission

Permitted opcodes: All other opcodes are permitted, including SLOAD, BALANCE, EXTCODESIZE, EXTCODECOPY, STATICCALL, CALLCODE, and arithmetic/logic opcodes. This allows leaf scripts to read chain state for time-lock and oracle-based conditions.

Gas schedule

OperationGas cost
Merkle proof verification (base)MERKLE_BASE_GAS (200)
Merkle proof verification (per level)MERKLE_GAS_PER_LEVEL (80)
Leaf script executionMetered per-opcode by EVM Berlin schedule, capped at MAX_LEAF_GAS (100,000)
Witness calldata (nonzero byte)16 gas (standard EIP-2028)
Witness calldata (zero byte)4 gas (standard EIP-2028)

Example costs for common configurations:

ConfigurationMerkle gasLeaf gas (ECDSA)Total verification
Single leaf (depth 0)200~3,000~3,200
4 leaves (depth 2)360~3,000~3,360
8 leaves (depth 3)440~3,000~3,440
256 leaves (depth 8)840~3,000~3,840

For comparison, current EOA transaction verification via ecrecover costs 3,000 gas. HCA verification cost is comparable for typical tree depths.

Performance impact

Transaction size: An HCA transaction includes the leaf script and Merkle proof as additional calldata. For a depth-3 tree with a 64-byte ECDSA leaf script and 65-byte signature, the witness adds approximately 225 bytes (64 + 96 + 65) compared to a standard 65-byte ECDSA signature. This overhead is linear in tree depth: 32 additional bytes per Merkle level.

Verification cost: Merkle proof verification requires two keccak256 operations per tree level (approximately 80 gas each). For typical trees (depth 2-8), total Merkle verification costs 360-840 gas — less than the 3,000 gas cost of a single ecrecover operation.

State overhead: One 32-byte authRoot field per HCA account. This is identical to the codeHash field already present in every account. The per-account state overhead is negligible.

Witness data: Unlike state, witness data is per-transaction and not persisted. Witness bytes are priced at standard calldata rates (16 gas/nonzero byte per EIP-2028).

authRoot rotation

An HCA account's authRoot can be updated via a dedicated rotation leaf — a leaf script whose sole purpose is authorizing replacement of the authRoot with a new value.

The rotation procedure:

  1. The transaction includes a witness spending a rotation-type leaf.
  2. The leaf script validates the rotation authorization (e.g., verifies a signature over the new authRoot).
  3. If the leaf script returns valid, the account's authRoot is replaced with the new value provided in the transaction's calldata.
  4. The account nonce is incremented.

The new authRoot MUST be a valid 32-byte value. The new authRoot MUST NOT be zero. The rotation transaction MUST NOT transfer value or execute external calls — it is an authRoot-only state change.

The rotation signing hash uses a dedicated domain tag to prevent cross-context replay:

rotation_hash = tagged_hash("HCARotate",
    chain_id || nonce || from || new_auth_root
)

Where chain_id and nonce are 8-byte big-endian, from is 20 bytes, and new_auth_root is 32 bytes.

Rotation enables key management operations: adding post-quantum leaves, removing compromised leaves, and upgrading the authentication policy — all without changing the account address.

Transaction signing hash

The signing hash commits to all replay-protection fields, the transaction calldata, and binds the signature to a specific spending condition:

signing_hash = tagged_hash("HCAWitness",
    chain_id || nonce || from || to || value ||
    data_len || data ||
    gas_limit || max_fee_per_gas || max_priority_fee_per_gas ||
    leaf_hash
)

Field encodings:

FieldEncodingSize
chain_idBig-endian8 bytes
nonceBig-endian8 bytes
fromRaw bytes20 bytes
toRaw bytes20 bytes
valueBig-endian16 bytes
data_lenBig-endian length of data8 bytes
dataRaw bytesVariable
gas_limitBig-endian8 bytes
max_fee_per_gasBig-endian16 bytes
max_priority_fee_per_gasBig-endian16 bytes
leaf_hashRaw bytes32 bytes

Including chain_id prevents cross-chain replay. Including from (sender address) prevents cross-account replay. Including nonce prevents same-chain replay. Including leaf_hash binds the signature to the specific spending condition used. Including data_len || data ensures transactions with different calldata produce different signing hashes.

Rationale

Why tagged hash with BIP-340 convention

The tagged_hash(tag, data) = keccak256(SHA256(tag) || SHA256(tag) || data) construction provides guaranteed domain separation between all HCA operations. The BIP-340 convention was chosen because it is well-analyzed, simple to implement, and already in production use across Bitcoin's Taproot ecosystem. The double SHA256 prefix is computed once per tag and can be cached, adding negligible overhead.

An alternative — using a single-byte prefix for domain separation — was considered and rejected because single-byte prefixes risk collisions with RLP-encoded data and do not provide the same formal separation guarantees.

Why single-hash address derivation

The address derivation formula tagged_hash("HCAAddr", auth_root)[12:] applies a single keccak256 computation with domain-separated input. An earlier draft used keccak256(tagged_hash("HCAAddr", auth_root))[12:] with an additional outer hash. Since tagged_hash already applies keccak256 internally, the outer hash was redundant — it added computational cost without improving security properties. The single-hash formula is simpler, faster, and sufficient: the domain separation guarantee comes from the tagged hash construction, not from hash nesting depth.

Why 20-byte addresses

HCA addresses use 20 bytes (tagged_hash(...)[12:]) for full backward compatibility with Ethereum's existing address format. All deployed contracts, ABIs, tooling, block explorers, and wallet software assume 20-byte addresses. The address type in Solidity is 20 bytes.

The security boundary is the 32-byte authRoot, not the 20-byte address. The address is a public identifier; authRoot is the cryptographic commitment. Truncating the identifier does not weaken the commitment.

If Ethereum adopts 32-byte addresses in the future, HCA address derivation can be extended to the full 32-byte tagged hash output in a companion EIP.

Why EVM bytecode leaves

Leaf scripts are EVM bytecode rather than a custom domain-specific language. This decision leverages the existing EVM infrastructure: all Ethereum clients already have EVM interpreters, developers already understand EVM bytecode, and the security model (gas metering, opcode restrictions) is well-understood.

A custom DSL would require new interpreter implementations in every client, new tooling, new security analysis, and new developer education — all for marginal benefit over the existing EVM.

Why depth-32 maximum

A maximum tree depth of 32 supports up to 2^32 (~4 billion) leaves, which exceeds any realistic authentication policy. The gas cost at depth 32 is approximately 2,760 gas for Merkle verification — still cheaper than ecrecover. Deeper trees provide no practical benefit and increase the theoretical maximum witness size.

Why CALL is banned statically

The EVM CALL opcode transfers value as a runtime parameter, making it impossible to distinguish value-bearing calls from zero-value calls through static bytecode analysis alone. Rather than introduce a complex runtime check within the restricted leaf execution context, all CALL invocations are banned statically. Leaf scripts that need to read external state can use STATICCALL, which is permitted and guarantees no state mutation or value transfer.

Why not EIP-8141 + CREATE2

A smart contract wallet deployed via CREATE2 with auth_root as the salt achieves functionally similar address-level commitment. However, this approach requires a factory contract deployment, proxy pattern management, and delegatecall overhead per transaction. A simple HCA account is cheaper to create (no deployment gas), cheaper to use (no proxy overhead), and simpler to understand (no factory/proxy abstraction).

HCA exists alongside EIP-8141 for the same reason BIP-360's P2MR exists alongside Taproot: a purpose-built primitive is simpler, cheaper, and clearer than an emergent pattern built on general-purpose infrastructure.

Why not ZK-STARK proof of key knowledge

ZK-STARK approaches prove knowledge of a private key preimage without revealing the public key on-chain. This protects existing EOA addresses retroactively. However, the address itself is still derived from keccak256(public_key)[12:] — the commitment remains to a public key, only hidden. STARK proofs cost approximately 9.5 million gas per transaction, compared to approximately 3,400 gas for an HCA transaction with a depth-2 tree.

ZK-STARKs are a migration tool for existing accounts. HCA is a construction for new accounts. They are complementary.

Why not hybrid PQC+EC address derivation

Hybrid approaches derive addresses from keccak256(PQ_pubkey || EC_pubkey)[12:]. This commits the address to both a classical and post-quantum public key. However, the address remains derived from public keys — both are eventually exposed. If the PQ scheme is later broken (e.g., lattice cryptanalysis advances), the address commitment is compromised. HCA's security rests on hash functions alone, which is the strongest available assumption against quantum speedup.

Why complement, not compete with EIP-8141

EIP-8141 enables existing accounts to upgrade their signature scheme. HCA enables new accounts to be born without a public key in the commitment chain. EIP-8141 is a migration mechanism. HCA is a native construction. Both are needed: EIP-8141 for the billions of dollars in existing accounts, HCA for every new account created after quantum safety becomes a protocol-level concern.

Backwards Compatibility

Existing accounts

HCA is fully opt-in. All existing EOAs, contract accounts, and EIP-7702 delegated accounts continue to function without modification. No existing transaction types are changed. No existing opcodes are modified.

msg.sender behavior

From the perspective of called contracts, an HCA account's msg.sender is a standard 20-byte address. Contracts receiving calls from HCA accounts cannot distinguish them from EOA calls. All existing contract interfaces, access control patterns, and msg.sender-based authorization continue to work.

Hard fork requirement

HCA requires a hard fork to introduce:

  1. A new account state field (authRoot).
  2. A new EIP-2718 transaction type (HCA_TX_TYPE).
  3. New transaction verification logic (Merkle proof + leaf execution).

This is consistent with other core EIPs that introduce new account types or transaction types.

State tree interaction

The authRoot field is stored in the account's state entry. If EIP-7864 is deployed, authRoot occupies a position in the unified binary tree alongside existing account fields. HCA's internal Merkle tree (for authentication policy) uses keccak256 and is independent of the state tree's hash function selection (BLAKE3 or Poseidon2). The two trees serve different purposes and do not interact.

Test Cases

Tagged hash domain separation

Input:

  • Tag: "HCAAddr"
  • Data: 0x0000000000000000000000000000000000000000000000000000000000000000 (32 zero bytes)

Expected: The output MUST differ from tagged_hash("HCALeaf", same_data), tagged_hash("HCABranch", same_data), tagged_hash("HCAWitness", same_data), and tagged_hash("HCARotate", same_data).

Address derivation

Input:

  • Leaves: [{version: 0x01, script: "OP_CHECKSIG_primary"}, {version: 0x01, script: "OP_CHECKSIG_recovery"}]
  • Tree: binary Merkle tree, depth 1

Procedure:

  1. Compute leaf_hash_0 = tagged_hash("HCALeaf", 0x01 || "OP_CHECKSIG_primary")
  2. Compute leaf_hash_1 = tagged_hash("HCALeaf", 0x01 || "OP_CHECKSIG_recovery")
  3. Compute auth_root = tagged_hash("HCABranch", leaf_hash_0 || leaf_hash_1)
  4. Compute address = tagged_hash("HCAAddr", auth_root)[12:]

Expected: A deterministic 20-byte address. Running the procedure twice with identical inputs MUST produce identical output.

Merkle proof verification

Input:

  • Tree with 4 leaves at depth 2
  • Proof for leaf at index 0

Expected: MerkleTree::verify(leaf_hash_0, proof_0, auth_root) returns true. MerkleTree::verify(leaf_hash_1, proof_0, auth_root) returns false (wrong leaf, correct proof).

Signing hash replay protection

Input:

  • Identical transaction fields on chain_id 1 (mainnet) and chain_id 11155111 (Sepolia)
  • Identical leaf_hash

Expected: The two signing hashes MUST differ. This verifies cross-chain replay protection.

Signing hash calldata sensitivity

Input:

  • Two transactions with identical fields except data: one with empty calldata, one with 0xdeadbeef
  • Identical leaf_hash

Expected: The two signing hashes MUST differ. This verifies that calldata is committed to in the signing hash.

Reference Implementation

A Rust reference implementation is available at github.com/eth-hca/hca-rs (v0.2.0). It includes:

  • Core modules: hash, merkle, address, witness, rlp, evm, builder
  • hca CLI with six commands: create-account, derive-address, generate-proof, verify-proof, encode-tx, signing-hash
  • Cross-implementation test vectors with hardcoded expected values
  • Property-based tests (proptest) and 4 libfuzzer fuzz targets
  • Constant-time proof verification (subtle crate)
  • Criterion benchmarks for all operations

Security Considerations

Formal security reduction

HCA's address security reduces to the preimage resistance of keccak256. Given address = tagged_hash("HCAAddr", auth_root)[12:], and since tagged_hash applies keccak256 with domain-separated input, recovering auth_root from address requires a preimage attack on keccak256 truncated to 160 bits: approximately 2^160 classical security and 2^80 under Grover's algorithm.

HCA's authentication security reduces to the collision resistance of the Merkle tree (2^128 birthday bound on keccak256) combined with the security of the chosen leaf signature scheme. The commitment structure (address derivation and Merkle tree) rests entirely on hash function assumptions — no elliptic curve mathematics, no lattice assumptions, no pairing-based cryptography.

Address collision analysis

HCA addresses occupy the same 20-byte address space as existing Ethereum addresses. Cross-type collision (HCA address = existing EOA or contract address) requires finding an auth_root such that tagged_hash("HCAAddr", auth_root)[12:] equals a target address. The tagged_hash domain separator ensures the hash input structure differs from EOA derivation (keccak256(pubkey)), CREATE derivation (keccak256(rlp([sender, nonce]))), and CREATE2 derivation (keccak256(0xff || factory || salt || init_code_hash)). This makes cross-type collision a preimage attack (~2^160), not a birthday attack.

Replay protection

Three replay vectors are mitigated:

VectorMitigation
Cross-chainchain_id included in signing hash
Same-chainnonce incremented per transaction
Cross-accountfrom (sender address) included in signing hash

Additionally, data (calldata) is included in the signing hash via data_len || data, ensuring transactions with different calldata produce different signatures.

This is identical to the replay protection model used by EIP-155 and EIP-8141 frame transactions.

Leaf-branch domain separation

The use of different tagged hash tags ("HCALeaf" vs "HCABranch") makes it impossible for a leaf hash to equal a branch hash for any input. This prevents second preimage attacks where an attacker attempts to substitute a leaf node for a branch node (or vice versa) in a Merkle proof.

Denial of service via leaf script execution

Unrestricted leaf script execution could enable denial-of-service attacks where an attacker submits transactions with computationally expensive leaf scripts that waste validator resources.

Mitigations:

  1. Gas cap: Leaf execution is capped at MAX_LEAF_GAS (100,000 gas). Gas is metered per-opcode according to the EVM Berlin schedule. Validators can bound the cost of verification.
  2. Banned opcodes: State-mutating and value-transferring opcodes are prohibited. Leaf scripts are pure verification functions.
  3. Pre-execution filter: Nodes verify the Merkle proof BEFORE executing the leaf script. Invalid proofs are rejected at the cost of proof verification only (~200-840 gas), not leaf execution.
  4. Precedent: ERC-4337 defined identical restrictions for UserOperation validation. EIP-8141's VERIFY frame runs in a read-only context with similar constraints.

Timing side-channel resistance

Merkle proof verification MUST use constant-time comparison for the final root equality check. A non-constant-time comparison could leak information about the authRoot value through timing measurements, potentially enabling targeted attacks against specific accounts.

Post-quantum signature scheme independence

HCA's commitment structure (address derivation + Merkle tree) is independent of any signature scheme. The security of the commitment layer depends solely on hash function properties (keccak256 preimage and collision resistance), which are resistant to quantum speedup — Grover's algorithm provides only a quadratic improvement, not the exponential break that Shor's provides against elliptic curve cryptography.

If a post-quantum signature scheme used inside a leaf script is later broken (e.g., advances in lattice cryptanalysis compromise Dilithium), the user rotates their authRoot to a new tree containing leaves with a different scheme. The commitment structure itself remains sound. This is a fundamental advantage over approaches that bake a specific PQ scheme into the address derivation.

User misconfiguration risks

HCA introduces configuration risks not present in current EOA accounts:

  • A user who creates a tree with a malformed leaf script locks their funds permanently.
  • A user who loses their Merkle tree backup cannot construct witnesses and loses access to their funds.
  • A user who creates a tree with no rotation leaf cannot update their authentication policy.
  • A user who creates a single-leaf tree with no recovery path has no fallback if that key is compromised.

Wallet safety requirements

To mitigate configuration risks, wallets implementing HCA account creation:

  1. MUST validate that all leaf scripts are well-formed EVM bytecode before deriving the address.
  2. MUST validate that all leaf scripts contain no banned opcodes and fit within MAX_LEAF_GAS.
  3. MUST simulate execution of each leaf script with test witness data to confirm at least one leaf is spendable.
  4. MUST require the user to confirm a backup of the complete Merkle tree (all leaves, structure, and associated keys).
  5. MUST warn if the tree contains no rotation leaf.
  6. MUST warn if the tree contains only a single leaf with no recovery path.
  7. SHOULD include a time-locked recovery leaf by default in standard wallet implementations.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth