--- eip: xxxx title: Historical Validity for ERC1271 Signatures description: Extension of ERC1271 to support historical signature validation. author: Mihir Wadekar (@mw2000), Kyle Kaplan (@kylekaplan), Sina Sabet (@sinasab) discussions-to: https://ethereum-magicians.org/t/erc1271-versioning-to-maintain-signature-validity/19681 status: Draft type: Standards Track category: ERC created: 2024-05-08 --- ## Abstract The proposed standard extends ERC-1271 to support historical signature validation. This creates a standard interface for onchain and offchain consumption of ERC-1271-based signatures that may have been valid in the past but no longer correspond to a current EIP-1271`isValidSignature` function. ## Motivation While externally owned account (EOA) signatures are perpetually valid, smart contract accounts (SCAs) can support updates to their signature validation logic, which means offchain EIP-1271 signatures that appear valid at a point in time are not guaranteed to be valid in the future. This inconsistency poses challenges for applications relying on these signatures to prove validity of attestations, statements, intents, or authorizations over time, particularly when interacting with SCAs that support rotating signers. ## Specification ### Functions #### `wasValidSignature` - **Purpose**: This function checks if a signature was valid at a specific point in time. - **Parameters**: - `_timestamp`: Timestamp at which the signature needs to be validated. - `_hash`: Hash of the data to be signed. - `_signature`: Signature byte array associated with the hash. - `_data`: Arbitrary extra data used for verification. - **Returns**: The bytes4 magic value `0x1626ba7e` if the signature is verified as valid. - **Requirements**: - MUST NOT modify state, ensuring it can be called safely by other contracts. - MUST allow external calls. ### Solidity interface ```solidity pragma solidity ^0.5.0; /// @title Example interface for historical validity functionality extending ERC1271 interface IERCXXX { /// @dev Should return whether the signature provided was valid at the time indicated by the timestamp. /// @param _timestamp The timestamp at which the signature needs to be validated. /// @param _hash Hash of the data to be signed. /// @param _signature Signature byte array associated with _hash. /// @param _data Arbitrary extra data that may be used by the verifier. /// @return The bytes4 magic value 0x1626ba7e when function passes /// MUST NOT modify state. /// MUST allow external calls. function wasValidSignature( uint64 _timestamp, bytes32 _hash, bytes memory _signature, bytes memory _data ) external view returns (bytes4); } ``` ## Rationale The introduction of historical validation mechanisms in EIP-1271 addresses a critical gap in the evolving landscape of SCAs and their interaction with digital signatures. As SCAs are updated over time, the logic governing signature validation can change, potentially rendering previously valid signatures invalid. This inconsistency poses challenges in scenarios where historical messages and signatures must remain verifiable indefinitely. ### Design Decisions - **wasValidSignature**: By allowing verification against a specific point-in-time via the `_timestamp` parameter, this function provides the flexibility needed for applications to confirm the validity of a signature that may not otherwise appear as valid. This is particularly important for SCA compatibility for attestation and other offchain-signature-based workflows, where the signature may not need to be verified by the chain until some future point in time, and the ability to retroactively invalidate signatures in this manner may be undesirable. The rest of the function's signature matches EIP-1271. ## Backwards Compatibility This EIP is designed to be fully compatible with EIP-1271 and does not affect existing implementations that do not require historical signature validation. ## Reference Implementation While this simple implementation requires data to be posted directly onchain to ensure its validity at a point in time, more opinionated flavors may take advantage of the extra _data parameter to allow verification via more bespoke patterns such as by providing [proofs of existence](https://gist.github.com/sina-wit/a9b5e5bc36cca7df699d9521263d03df). ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /// @title ERCXXXX /// @dev Contract for managing digital signatures with timestamped verifiers import {IERC1271} from "./interfaces/IERC1271.sol"; import {IERCXXXX} from "./interfaces/IERCXXXX.sol"; import {Owned} from "solmate/src/auth/Owned.sol"; import {SignatureCheckerLib} from "solady/src/utils/SignatureCheckerLib.sol"; /// @notice Contract that implements the IERCXXXX interface contract ERCXXXX is IERCXXXX, Owned { /// @notice Mapping of approved hashes to their timestamps /// @dev If timestamp is 0, then the hash is not approved mapping(bytes32 => uint256) public approvedHashes; /// @notice Constant for the magic value returned for a valid signature bytes4 constant MAGICVALUE = bytes4(0x1626ba7e); /// @notice Contract constructor constructor() Owned(msg.sender) {} /// @notice Checks if the signature was valid at a given timestamp /// @param _timestamp The timestamp to check the signature against /// @param _hash The hash of the data /// @param _signature The signature to validate /// @param _data Additional data to validate the signature /// @return magicValue Standard magic value for a valid signature, 0x00000000 for an invalid signature function wasValidSignature(uint64 _timestamp, bytes32 _hash, bytes memory _signature, bytes memory _data) public view override returns (bytes4) { // If the hash is not approved, return 0x00000000 if (approvedHashes[_hash] == 0) { return 0x00000000; } // If the hash was approved before or at the given timestamp, return the magic value else if (approvedHashes[_hash] <= _timestamp) { return MAGICVALUE; } // If the hash was approved after the given timestamp, return 0x00000000 else { return 0x00000000; } } /// @notice Approves a hash /// @dev Only the owner can approve a hash /// @param hash The hash to approve function setApproveHash(bytes32 hash) public onlyOwner { approvedHashes[hash] = block.timestamp; } } ``` ## Security Considerations Care must be taken to ensure that the update mechanism for the signature validation logic is secure against unauthorized changes. Additionally, the storage method chosen must be optimized to prevent excessive gas costs associated with storing historical data.