// 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; } }