ERC-8228 — Cryptographic Amnesia

Summary: ERC-8228 defines a protocol for governance-controlled, cryptographically verifiable data destruction. A multi-party ceremony collects shares of a master key, time-locks them behind a VDF, then destroys both the key and the data it protects. A ZK proof of erasure is generated on-chain so any verifier can confirm destruction without re-executing it.

OMEGA V4 audit finding KSR-CVN-001 (Critical) identified a phase enforcement bypass. All ERC-8228 implementations in Covenant V0.7 include the enforced phase state machine required by the finding’s resolution.

Covenant Surface

ConstructDescription
amnesia { }Block of code whose state is eligible for destruction
ceremony { }Multi-party key gathering ceremony definition
destroy(field)Intrinsic to irreversibly erase a field
on_destroyLifecycle hook called before destruction
@amnesia_eligibleAnnotate a field as destructible

Phase State Machine

An ERC-8228 ceremony progresses through exactly five phases. Invalid transitions revert:

IDLE → GATHERING → FINALIZED → LOCKED → DESTROYED
PhaseTransitions toTrigger
IDLEGATHERINGceremony.begin() called by authorized initiator
GATHERINGFINALIZEDThreshold of shares submitted
GATHERINGIDLEceremony.abort() before threshold
FINALIZEDLOCKEDVDF computation completed (off-chain), proof submitted
LOCKEDDESTROYEDVDF time-lock expired + ceremony.execute()
DESTROYED(terminal)

KSR-CVN-001 fix: Before V0.7.1, a reentrancy pattern allowed callers to skip FINALIZED → LOCKED and jump directly to DESTROYED. The fix adds a phase_guard check at the top of execute() using a CEI (Checks-Effects-Interactions) pattern enforced by the compiler.

Shamir Secret Sharing

The master destruction key is split using Shamir’s Secret Sharing over GF(2^256). The ceremony parameters are specified in the contract:

ceremony DestroyMasterKey {
    shares: 5
    threshold: 3
    time_lock: 30 days
    participants: [admin1, admin2, admin3, admin4, admin5]
    
    on_destroy {
        destroy(user_data)
        destroy(private_keys)
        emit DataDestroyed(block.timestamp)
    }
}

This compiles to:

  • threshold = 3, shares = 5 — 3 of 5 participants must submit shares
  • Each participant receives a share off-chain (computed at deployment time)
  • When 3 shares are submitted on-chain, the master key reconstruction begins

The shares are committed as Pedersen commitments on-chain. The contract verifies each submitted share against the commitment before accepting it.

VDF Time-Lock

After shares reach threshold, a Verifiable Delay Function prevents immediate destruction. This ensures:

  • A governance veto window exists even after the threshold is reached
  • Time-lock cannot be shortcut by faster hardware (VDF is inherently sequential)

ERC-8228 uses the Wesolowski VDF construction:

  • Input: hash of collected shares + block hash at threshold
  • Delay: configurable (minimum 24h recommended, 30 days in example above)
  • Output: VDF proof (y, π) where y = g^(2^T) mod N

The VDF computation happens off-chain by any participant. The on-chain contract only verifies the proof (cheap) not runs it (expensive). Verification via the 0x22 precompile costs ~45,000 gas.

ZK Proofs of Destruction

After DESTROYED, the contract generates a proof of erasure — a ZK statement that:

  1. The fields listed in destroy() contained non-zero values before the ceremony
  2. Those storage slots are now zero
  3. The transition was authorized by the correct threshold of shares

The proof is a Groth16 proof (~200 bytes) stored in contract metadata:

{
  "type": "amnesia_erasure",
  "erc": "8228",
  "destroyed_slots": ["0x01", "0x02"],
  "ceremony_block": 21847392,
  "proof": "0x1f2a...c4d9"
}

Any verifier can check this proof on-chain using the 0x23 precompile without replaying the destruction.

on_destroy Lifecycle Hook

The on_destroy hook runs immediately before storage is zeroed. Use it to emit events, notify dependent contracts, or settle final state:

amnesia {
    field secret_key: bytes32 @amnesia_eligible
    field user_mapping: map<address, bytes32> @amnesia_eligible
    
    on_destroy {
        emit SecretDestroyed(block.timestamp)
        // settle any outstanding claims first
        for addr in pending_settlements {
            settle(addr)
        }
        destroy(secret_key)
        destroy(user_mapping)
    }
}

Warning: Any external call in on_destroy is a reentrancy risk if the called contract is untrusted. The compiler emits warning W-AMN-001 if an external call precedes destroy() without a reentrancy lock.

Precompile Addresses

AddressNameDescriptionGas
0x20amnesia_destroyZero storage slots with proof30,000 + 5,000/slot
0x21shamir_verifyVerify a Shamir share commitment8,000
0x22vdf_verifyVerify Wesolowski VDF proof45,000
0x23erasure_proof_verifyVerify stored Groth16 erasure proof180,000
0x24phase_transitionAtomic phase state transition with event5,000

Gas Table

OperationGas cost
ceremony.begin()50,000
Submit share25,000
ceremony.finalize() (at threshold)80,000
Submit VDF proof45,000 + storage
ceremony.execute() (destroy N fields)30,000 + 5,000×N
Erasure proof verification180,000

See Also