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

/**
 * @title AereConsensusPQCAttestor, post-quantum FINALITY-ATTESTATION layer for AERE (chain 2800)
 *
 * @notice HONEST SCOPE. This contract does NOT replace Besu QBFT block signing.
 *         AERE blocks are still produced and signed by Besu validators with
 *         ECDSA (secp256k1), exactly as today. Replacing that internal signature
 *         would require forking the Besu Java consensus engine, which is out of
 *         scope. Instead, this contract adds a post-quantum FINALITY ATTESTATION
 *         layer that runs ALONGSIDE QBFT: each registered validator additionally
 *         signs the hash of a finalized block with Falcon-512 (a NIST PQC
 *         lattice signature), and submits that signature here. The signature is
 *         verified in full, on-chain, by the real AereFalcon512Verifier. When at
 *         least two thirds of the registered validators have submitted a valid
 *         Falcon-512 signature over the same (blockNumber, blockHash), the block
 *         is marked POST-QUANTUM FINALIZED and an event is emitted.
 *
 *         This is NOT "post-quantum consensus". It is "post-quantum finality
 *         attestation": an independent, quantum-resistant record that a
 *         supermajority of validators vouched for a given block, whose validity
 *         does not depend on the hardness of the discrete-log problem.
 *
 *         Decentralization tracks the validator set. With the three Foundation
 *         validators live today, this attestation is exactly as centralized as
 *         AERE consensus is today; it decentralizes as the validator set grows
 *         (path to 7, then 21). There is no admin backdoor on the finality
 *         record: the owner (Foundation) can only register validator public
 *         keys, and can never mark a block finalized or forge an attestation.
 *         The only way a block becomes post-quantum finalized is a real
 *         two-thirds set of on-chain-verified Falcon-512 signatures.
 */

interface IFalcon512Verifier {
    /// @notice Full on-chain Falcon-512 verification (SHAKE256 HashToPoint,
    ///         14-bit pk decode, comp_decode, negacyclic NTT multiply mod
    ///         q=12289, centering, l2-norm bound). Returns true iff valid.
    function verify(
        bytes memory pk,
        bytes memory message,
        bytes memory nonce,
        bytes memory compSig
    ) external view returns (bool);
}

contract AereConsensusPQCAttestor {
    // ----- Falcon-512 encoding constants -----
    uint256 public constant NONCE_LEN = 40; // Falcon salt length
    uint256 public constant PUBKEY_LEN = 897; // NIST Falcon-512 public key length
    uint8 public constant PK_HEADER = 0x09; // 0x00 + logn, logn = 9

    // ----- wiring -----
    IFalcon512Verifier public immutable verifier;
    address public owner;

    // ----- validator registry -----
    uint256 public totalValidators;
    mapping(address => bool) public isValidator;
    mapping(address => bytes) public falconPubKey; // validator -> 897-byte Falcon-512 pk

    // ----- attestation records (keyed by keccak256(blockNumber, blockHash)) -----
    mapping(bytes32 => uint256) public attestCount;
    mapping(bytes32 => bool) public postQuantumFinalized;
    mapping(bytes32 => uint256) public finalizedAtBlock; // chain block number the quorum was reached at
    mapping(bytes32 => mapping(address => bool)) public hasAttested;

    // ----- events -----
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event ValidatorRegistered(address indexed validator, uint256 pubKeyLen, uint256 totalValidators);
    event ValidatorKeyRotated(address indexed validator, uint256 pubKeyLen);
    event BlockAttested(
        address indexed validator,
        address relayer,
        uint256 indexed blockNumber,
        bytes32 indexed blockHash,
        uint256 attestCount,
        uint256 totalValidators
    );
    event PostQuantumFinalized(
        uint256 indexed blockNumber,
        bytes32 indexed blockHash,
        uint256 attestCount,
        uint256 totalValidators
    );

    modifier onlyOwner() {
        require(msg.sender == owner, "not owner");
        _;
    }

    constructor(address verifierAddr) {
        require(verifierAddr != address(0), "verifier=0");
        verifier = IFalcon512Verifier(verifierAddr);
        owner = msg.sender;
        emit OwnershipTransferred(address(0), msg.sender);
    }

    // ==========================================================================
    //                         Foundation: validator registry
    // ==========================================================================

    /// @notice Register a validator's Falcon-512 public key, or rotate an
    ///         existing one. Foundation only. Registering a NEW validator raises
    ///         totalValidators (and therefore the two-thirds quorum); rotating an
    ///         existing validator's key leaves the count unchanged.
    /// @param validator the validator's chain address (as in the QBFT set).
    /// @param pubKey    the 897-byte NIST Falcon-512 public key (0x09 header).
    function registerValidator(address validator, bytes calldata pubKey) external onlyOwner {
        require(validator != address(0), "validator=0");
        require(pubKey.length == PUBKEY_LEN, "pubkey len != 897");
        require(uint8(pubKey[0]) == PK_HEADER, "pubkey header != 0x09");

        falconPubKey[validator] = pubKey;
        if (!isValidator[validator]) {
            isValidator[validator] = true;
            totalValidators += 1;
            emit ValidatorRegistered(validator, pubKey.length, totalValidators);
        } else {
            emit ValidatorKeyRotated(validator, pubKey.length);
        }
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "newOwner=0");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    // ==========================================================================
    //                     Validator: attest a finalized block
    // ==========================================================================

    /// @notice Submit a validator's Falcon-512 attestation over a finalized block.
    ///
    ///         POST-QUANTUM AUTHORSHIP. The attestation is bound to `validator`
    ///         solely by verifying the Falcon-512 signature against that
    ///         validator's REGISTERED Falcon public key, in full, on-chain. It is
    ///         deliberately NOT bound to the ECDSA transaction sender: secp256k1
    ///         is exactly what a quantum computer breaks, so a post-quantum
    ///         attestation whose authorship rested on the tx signer would defeat
    ///         its own purpose. `msg.sender` is therefore an untrusted relayer
    ///         that only pays gas; it cannot forge an attestation, because it
    ///         cannot produce a valid Falcon-512 signature for a key it does not
    ///         hold. Each validator can relay its own attestation or delegate the
    ///         relay; the cryptographic authorship is the Falcon signature.
    ///
    ///         On the two-thirds threshold the block is marked post-quantum
    ///         finalized.
    /// @param blockNumber the finalized block's number.
    /// @param blockHash   the finalized block's canonical hash (the signed message).
    /// @param validator   the registered validator whose Falcon key signed this.
    /// @param falconSig   nonce(40 bytes) || compSig (the Falcon comp_encode body,
    ///                    i.e. the NIST esig with its 0x29 header byte stripped).
    function attestBlock(
        uint256 blockNumber,
        bytes32 blockHash,
        address validator,
        bytes calldata falconSig
    ) external {
        require(isValidator[validator], "not a registered validator");
        require(falconSig.length > NONCE_LEN, "sig too short");

        bytes32 key = attestationKey(blockNumber, blockHash);
        require(!hasAttested[key][validator], "already attested");

        bytes memory nonce = falconSig[0:NONCE_LEN];
        bytes memory compSig = falconSig[NONCE_LEN:];
        bytes memory message = abi.encodePacked(blockHash); // exactly 32 bytes

        // Full, real Falcon-512 verification on-chain. No shortcut, no trust.
        bool ok = verifier.verify(falconPubKey[validator], message, nonce, compSig);
        require(ok, "falcon verify failed");

        hasAttested[key][validator] = true;
        uint256 count = attestCount[key] + 1;
        attestCount[key] = count;
        emit BlockAttested(validator, msg.sender, blockNumber, blockHash, count, totalValidators);

        // Two-thirds quorum: count * 3 >= totalValidators * 2 (i.e. count >= ceil(2N/3)).
        if (!postQuantumFinalized[key] && count * 3 >= totalValidators * 2 && totalValidators > 0) {
            postQuantumFinalized[key] = true;
            finalizedAtBlock[key] = block.number;
            emit PostQuantumFinalized(blockNumber, blockHash, count, totalValidators);
        }
    }

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

    /// @notice The record key for a (blockNumber, blockHash) pair.
    function attestationKey(uint256 blockNumber, bytes32 blockHash) public pure returns (bytes32) {
        return keccak256(abi.encode(blockNumber, blockHash));
    }

    /// @notice Number of validator Falcon-512 attestations recorded for a block.
    function getAttestCount(uint256 blockNumber, bytes32 blockHash) external view returns (uint256) {
        return attestCount[attestationKey(blockNumber, blockHash)];
    }

    /// @notice True once a two-thirds set of validators has post-quantum finalized the block.
    function isPostQuantumFinalized(uint256 blockNumber, bytes32 blockHash) external view returns (bool) {
        return postQuantumFinalized[attestationKey(blockNumber, blockHash)];
    }

    /// @notice Whether a specific validator has attested a given block.
    function hasValidatorAttested(uint256 blockNumber, bytes32 blockHash, address validator)
        external
        view
        returns (bool)
    {
        return hasAttested[attestationKey(blockNumber, blockHash)][validator];
    }

    /// @notice The minimum number of attestations required for post-quantum
    ///         finality at the current validator count: ceil(2 * totalValidators / 3).
    function quorumThreshold() public view returns (uint256) {
        if (totalValidators == 0) return 0;
        return (2 * totalValidators + 2) / 3;
    }
}
