// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
 * @title AerePQAttestationKeyRegistry, the post-quantum finality-attestation key
 *        registry for Aere Network validators (chain 2800).
 *
 * @notice This registry holds, for each Aere Network validator, a HASH-BASED
 *         attestation public key (Winternitz-XMSS / leanSig style, one-time or
 *         few-time hash-based signatures). A hash-based signature is post-quantum
 *         from hash collision and preimage resistance ALONE, so it survives a
 *         cryptographically-relevant quantum adversary that can forge the classical
 *         ECDSA (secp256k1) seals Aere consensus uses. This key is SEPARATE from a
 *         validator's ECDSA consensus key: it is the PQ FINALITY key, used only for
 *         an ADDITIVE post-quantum attestation over each finalized block.
 *
 * @notice The registry produces a deterministic `validatorSetRoot()`, a binding
 *         commitment to the enrolled validators and their current attestation keys.
 *         The off-chain aggregator reconstructs this same root, and
 *         AereFinalityCertificateVerifier binds every certificate to it, so a
 *         certificate proven against a stale or forged key set is rejected on-chain.
 *
 * @dev    THE HONEST SCOPE BOUNDARY, read before trusting or describing anything.
 *
 *         1. THIS DOES NOT CHANGE AERE CONSENSUS. Blocks are STILL produced and
 *            finalized by Besu QBFT validators signing classical ECDSA (secp256k1)
 *            committed seals. This registry, and the certificate that reads it, are
 *            PURELY ADDITIVE: validators keep signing ECDSA for consensus and ALSO
 *            sign a hash-based attestation over each finalized block. Nothing here
 *            flips consensus, and PQ finality is not live. Never imply consensus is
 *            post-quantum.
 *
 *         2. NO ON-CHAIN HASH-BASED VERIFICATION HAPPENS HERE. This contract stores
 *            attestation public keys and commits to them in a root. The hash-based
 *            signatures themselves are verified OFF-CHAIN, inside a zkVM aggregation
 *            circuit (the [MEASURE] research component, see
 *            docs/AERE-PQ-FINALITY-CERTIFICATE.md), which recomputes this root from
 *            the witnessed keys. This registry is the on-chain anchor for that root.
 *
 *         3. APPEND-ONLY, FAIL-CLOSED, NO SILENT REWRITE. Every key ever registered
 *            is retained in `_keys` and reachable via the history views. A rotation
 *            never overwrites a key's material: it APPENDS a new record, marks the
 *            predecessor superseded (a metadata flag, not a deletion), and advances a
 *            monotonic per-validator `keyEpoch`. One-time and few-time hash-based keys
 *            MUST rotate as their signing capacity is used, so rotation is expected and
 *            cheap. A validator may only register its OWN key (msg.sender == validator);
 *            the owner cannot forge a validator's PQ key.
 *
 *         4. GOVERNED MEMBERSHIP, SELF-CUSTODIED KEYS. The owner (Aere Foundation,
 *            later the Timelock) enrolls the addresses that ARE validators, mirroring
 *            how QBFT consensus enrollment is curated off-chain via the Foundation
 *            Ledger and QBFT extraData. Enrollment defines WHO counts toward the
 *            finality quorum and fixes the root ordering. Each validator then registers
 *            its own attestation key. There is no fund custody and no key-forgery path.
 */
contract AerePQAttestationKeyRegistry is Ownable {
    // ==========================================================================
    //           Hash-based scheme labels (post-quantum from hashing alone)
    // ==========================================================================
    //
    // These name the hash-based signature family a validator's attestation key uses.
    // They are hash-based, so their post-quantum security rests only on the collision
    // and preimage resistance of the underlying hash, NOT on any lattice or discrete-log
    // assumption. The scheme is recorded and committed into the root; the actual
    // signature verification happens in the off-chain aggregation circuit.

    uint8 public constant SCHEME_XMSS = 1; // RFC 8391 XMSS (stateful, few-time hash-based)
    uint8 public constant SCHEME_WOTS = 2; // Winternitz OTS+ (one-time hash-based)
    uint8 public constant SCHEME_LEANSIG = 3; // leanSig / lean hash-based (Ethereum Lean Consensus lineage)
    uint8 public constant SCHEME_SLHDSA128S = 4; // SLH-DSA-128s (FIPS 205, stateless hash-based)
    uint8 public constant SCHEME_XMSSMT = 5; // XMSS^MT (RFC 8391 multi-tree, more few-time capacity)

    /// @dev Sane bounds for a hash-based attestation public key. Hash-based keys are
    ///      SMALL (an XMSS public key is a hash root plus parameters, tens of bytes),
    ///      so the generous upper bound only rejects obviously malformed input.
    uint256 internal constant MIN_PUBKEY_LEN = 32;
    uint256 internal constant MAX_PUBKEY_LEN = 2048;

    /// @dev Sentinel for "this validator has no current key".
    uint256 internal constant NO_KEY = type(uint256).max;

    // ==========================================================================
    //                                  Types
    // ==========================================================================

    /// @notice An append-only attestation-key record. Never mutated except to flag it
    ///         superseded when the owning validator rotates to a successor.
    struct KeyRecord {
        address validator; // the validator that registered this key
        uint8 scheme; // one of SCHEME_*
        uint32 epoch; // this validator's key-epoch when the key was registered (1-based)
        uint64 registeredBlock; // block the key was registered at
        uint64 supersededBlock; // block a successor superseded it (0 while current)
        bool superseded; // true once a successor key was registered
        bytes pubKey; // the raw hash-based attestation public key
    }

    /// @notice Per-validator membership + current-key pointer.
    struct Validator {
        bool enrolled; // true once the owner enrolled this address
        uint32 index; // position in `_validators` (fixes the root ordering)
        uint32 keyEpoch; // 0 = no key yet; increments on each (re)registration
        uint256 currentKeyId; // NO_KEY until the first key is registered
    }

    // ==========================================================================
    //                                 Storage
    // ==========================================================================

    address[] private _validators; // enrollment order == root ordering
    mapping(address => Validator) private _validatorInfo;
    KeyRecord[] private _keys; // append-only; keyId is the array index
    mapping(address => uint256[]) private _validatorKeyHistory; // all keyIds per validator

    // ==========================================================================
    //                                 Events
    // ==========================================================================

    event ValidatorEnrolled(address indexed validator, uint32 indexed index);
    event AttestationKeyRegistered(
        address indexed validator, uint256 indexed keyId, uint8 indexed scheme, uint32 epoch, uint256 supersededKeyId
    );

    // ==========================================================================
    //                                 Errors
    // ==========================================================================

    error AlreadyEnrolled(address validator);
    error NotEnrolled(address validator);
    error ZeroValidator();
    error InvalidScheme(uint8 scheme);
    error InvalidPubKey();
    error UnknownKey(uint256 keyId);

    // ==========================================================================
    //                          Owner: set membership
    // ==========================================================================

    /**
     * @notice Enroll `validator` into the Aere Network finality-attestation validator
     *         set. Owner only. Enrollment defines WHO counts toward the PQ finality
     *         quorum and fixes this validator's position in the root ordering. It does
     *         NOT register a key: the validator registers its own attestation key next.
     * @dev    Idempotent-guarded: re-enrolling a known validator reverts AlreadyEnrolled,
     *         so the set is append-only and the ordering never silently shifts.
     */
    function enrollValidator(address validator) external onlyOwner {
        if (validator == address(0)) revert ZeroValidator();
        Validator storage v = _validatorInfo[validator];
        if (v.enrolled) revert AlreadyEnrolled(validator);

        uint32 index = uint32(_validators.length);
        _validators.push(validator);
        v.enrolled = true;
        v.index = index;
        v.keyEpoch = 0;
        v.currentKeyId = NO_KEY;

        emit ValidatorEnrolled(validator, index);
    }

    // ==========================================================================
    //                     Validator: register / rotate own key
    // ==========================================================================

    /**
     * @notice Register (or rotate to) the caller's hash-based attestation public key.
     *         Callable ONLY by an enrolled validator, for its OWN key. The first call
     *         sets key-epoch 1; each later call appends a fresh record, marks the
     *         previous key superseded, and advances the key-epoch, because one-time and
     *         few-time hash-based keys must rotate as their signing capacity is spent.
     *         Append-only: nothing is overwritten or deleted, and the full history is
     *         queryable.
     * @param scheme one of SCHEME_* (a hash-based family).
     * @param pubKey the raw hash-based attestation public key (length-checked only).
     * @return keyId the identifier assigned to the new key.
     */
    function registerAttestationKey(uint8 scheme, bytes calldata pubKey) external returns (uint256 keyId) {
        Validator storage v = _validatorInfo[msg.sender];
        if (!v.enrolled) revert NotEnrolled(msg.sender);
        _validateKey(scheme, pubKey);

        uint256 supersededKeyId = NO_KEY;
        if (v.currentKeyId != NO_KEY) {
            supersededKeyId = v.currentKeyId;
            KeyRecord storage old = _keys[supersededKeyId];
            old.superseded = true;
            old.supersededBlock = uint64(block.number);
        }

        uint32 newEpoch = v.keyEpoch + 1;
        keyId = _keys.length;
        _keys.push(
            KeyRecord({
                validator: msg.sender,
                scheme: scheme,
                epoch: newEpoch,
                registeredBlock: uint64(block.number),
                supersededBlock: 0,
                superseded: false,
                pubKey: pubKey
            })
        );
        v.keyEpoch = newEpoch;
        v.currentKeyId = keyId;
        _validatorKeyHistory[msg.sender].push(keyId);

        emit AttestationKeyRegistered(msg.sender, keyId, scheme, newEpoch, supersededKeyId);
    }

    function _validateKey(uint8 scheme, bytes calldata pubKey) internal pure {
        if (scheme < SCHEME_XMSS || scheme > SCHEME_XMSSMT) revert InvalidScheme(scheme);
        if (pubKey.length < MIN_PUBKEY_LEN || pubKey.length > MAX_PUBKEY_LEN) revert InvalidPubKey();
    }

    // ==========================================================================
    //                        Validator-set commitment (root)
    // ==========================================================================

    /**
     * @notice The deterministic commitment to the current validator set and their
     *         current attestation keys. This is the value AereFinalityCertificateVerifier
     *         binds every certificate to, and the value the off-chain aggregation circuit
     *         MUST recompute from the witnessed keys.
     *
     *         Definition (a flat, ordered keccak commitment):
     *           leaf_i = keccak256(abi.encode(validator_i, keyEpoch_i, keyHash_i))
     *           root   = keccak256(abi.encode(leaf_0, leaf_1, ..., leaf_{N-1}))
     *         where keyHash_i = keccak256(currentPubKey_i), or bytes32(0) if validator_i
     *         has not registered a key yet, and the ordering is enrollment order.
     *
     * @dev    Because the root commits to each validator's address AND its current
     *         key-epoch AND its current key material, ANY rotation (which bumps the
     *         epoch and changes the key hash) changes the root. A certificate proven
     *         against the pre-rotation root therefore stops matching automatically. A
     *         Merkle tree is a drop-in alternative if inclusion proofs are wanted; a
     *         flat commitment is sufficient because the circuit witnesses the whole set.
     */
    function validatorSetRoot() public view returns (bytes32) {
        uint256 n = _validators.length;
        bytes32[] memory leaves = new bytes32[](n);
        for (uint256 i = 0; i < n; i++) {
            address a = _validators[i];
            Validator storage v = _validatorInfo[a];
            bytes32 keyHash = v.currentKeyId == NO_KEY ? bytes32(0) : keccak256(_keys[v.currentKeyId].pubKey);
            leaves[i] = keccak256(abi.encode(a, v.keyEpoch, keyHash));
        }
        return keccak256(abi.encode(leaves));
    }

    // ==========================================================================
    //                                  Views
    // ==========================================================================

    /// @notice Number of enrolled validators (== N, the set size the quorum is over).
    function validatorCount() external view returns (uint256) {
        return _validators.length;
    }

    /// @notice The enrolled validator at a root-ordering position. Reverts if out of range.
    function validatorAt(uint256 index) external view returns (address) {
        if (index >= _validators.length) revert UnknownKey(index);
        return _validators[index];
    }

    /// @notice The full enrolled validator set in root order.
    function validators() external view returns (address[] memory) {
        return _validators;
    }

    /// @notice True iff `validator` is enrolled in the set.
    function isEnrolled(address validator) external view returns (bool) {
        return _validatorInfo[validator].enrolled;
    }

    /// @notice The number of enrolled validators that currently have an attestation key.
    function activeKeyCount() public view returns (uint256 count) {
        uint256 n = _validators.length;
        for (uint256 i = 0; i < n; i++) {
            if (_validatorInfo[_validators[i]].currentKeyId != NO_KEY) count++;
        }
    }

    /// @notice True iff EVERY enrolled validator has registered a current attestation key.
    ///         A certificate is only meaningful once the set is complete (a consumer that
    ///         gates on PQ finality SHOULD check this alongside the root).
    function isSetComplete() external view returns (bool) {
        uint256 n = _validators.length;
        if (n == 0) return false;
        return activeKeyCount() == n;
    }

    /// @notice The current attestation key of `validator`. Reverts NotEnrolled if the
    ///         address is not a validator; returns keyId == NO_KEY sentinel fields if the
    ///         validator has not registered a key yet.
    function currentKey(address validator)
        external
        view
        returns (uint256 keyId, uint8 scheme, uint32 epoch, bytes memory pubKey)
    {
        Validator storage v = _validatorInfo[validator];
        if (!v.enrolled) revert NotEnrolled(validator);
        keyId = v.currentKeyId;
        if (keyId == NO_KEY) return (NO_KEY, 0, 0, "");
        KeyRecord storage k = _keys[keyId];
        return (keyId, k.scheme, k.epoch, k.pubKey);
    }

    /// @notice The current key-epoch of `validator` (0 = no key registered yet).
    function keyEpochOf(address validator) external view returns (uint32) {
        return _validatorInfo[validator].keyEpoch;
    }

    /// @notice Total number of key records ever registered (keyIds run 0..keyCount-1).
    function keyCount() external view returns (uint256) {
        return _keys.length;
    }

    /// @notice Read an append-only key record by id. Reverts UnknownKey on a bad id.
    function getKey(uint256 keyId)
        external
        view
        returns (
            address validator,
            uint8 scheme,
            uint32 epoch,
            uint64 registeredBlock,
            uint64 supersededBlock,
            bool superseded,
            bytes memory pubKey
        )
    {
        if (keyId >= _keys.length) revert UnknownKey(keyId);
        KeyRecord storage k = _keys[keyId];
        return (k.validator, k.scheme, k.epoch, k.registeredBlock, k.supersededBlock, k.superseded, k.pubKey);
    }

    /// @notice All keyIds ever registered by `validator`, oldest first (append-only history).
    function keyHistoryOf(address validator) external view returns (uint256[] memory) {
        return _validatorKeyHistory[validator];
    }
}
