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

/**
 * @title AerePQCAccount, a post-quantum-secured ERC-4337 smart account (roadmap #270)
 * @notice An ERC-4337 v0.7 smart account whose sole owner is a NIST Falcon-512
 *         public key (897 bytes, the 0x09-headed NIST encoding). Every
 *         authorization, whether a bundled UserOperation or an EIP-1271
 *         off-chain signature check, is decided by the LIVE, spec-complete
 *         on-chain AereFalcon512Verifier (chain 2800: 0x4E8e9682...D8fFC): full
 *         SHAKE256 HashToPoint, 14-bit public-key decode, compressed-signature
 *         (comp_decode) decode, negacyclic NTT multiply mod q=12289 in
 *         Z_q[x]/(x^512+1), centering, and l2-norm bound check. There is no
 *         classical ECDSA fallback: the account is controlled by, and only by, a
 *         quantum-resistant lattice key.
 *
 *         This turns AERE's on-chain PQC verifier suite into a usable wallet
 *         primitive: a user can hold an account whose spending authority is a
 *         Falcon-512 key, verified by real lattice cryptography on chain.
 *
 * @dev    SIGNATURE ENVELOPE. Wherever this account consumes a Falcon signature
 *         (validateUserOp, isValidSignature, executeWithFalcon), the `signature`
 *         bytes are `abi.encode(bytes nonce, bytes compSig)` where `nonce` is the
 *         40-byte Falcon salt and `compSig` is the compressed s2 encoding
 *         (comp_encode output, the esig body WITHOUT the 0x29 header byte). The
 *         signed message is DOMAIN-SEPARATED: a fund-moving authorization
 *         (validateUserOp, executeWithFalcon) signs `abi.encodePacked(domain, hash)`
 *         (64 bytes, domain = USEROP_DOMAIN / EXEC_DOMAIN), while the EIP-1271
 *         isValidSignature surface signs the raw 32 bytes `abi.encodePacked(hash)`.
 *         The two message shapes can never collide, so a signature obtained through
 *         a dapp "login" (isValidSignature over an attacker-chosen hash) can NOT be
 *         replayed as a spend. For a UserOperation the hash is the ERC-4337
 *         userOpHash, which already binds sender + chainid.
 *
 * @dev    GAS / EIP-7825. A Falcon-512 verify is roughly 10.5M gas. A full
 *         handleOps → validateUserOp path therefore runs as a single ordinary
 *         transaction that must stay under the Fusaka EIP-7825 per-transaction
 *         cap of 2^24 = 16,777,216 gas; the deploy/proof script measures the
 *         real number. `isValidSignature` is a `view`, so integrators can verify
 *         a Falcon signature for free via eth_call regardless of the cap. A
 *         native SHAKE/keccak precompile would cut the per-auth cost sharply
 *         (tracked as a separate roadmap item); today PQC auth is heavy but real.
 *
 *         No owner, no admin, no upgrade beyond the one-shot factory initializer.
 */
interface IFalcon512Verifier {
    /// @notice Full on-chain Falcon-512 verification. Returns true iff
    ///         (pk, message, nonce, compSig) is a valid Falcon-512 signature.
    function verify(
        bytes memory pk,
        bytes memory message,
        bytes memory nonce,
        bytes memory compSig
    ) external view returns (bool);
}

contract AerePQCAccount {
    /// @notice The 897-byte NIST Falcon-512 public key that owns this account.
    bytes public falconPubKey;

    /// @notice The LIVE AereFalcon512Verifier this account delegates verification to.
    IFalcon512Verifier public falcon;

    /// @notice ERC-4337 EntryPoint allowed to call validateUserOp + executeFromEntryPoint.
    address public entryPoint;

    /// @notice Factory that deployed this account (gates the one-shot initializer).
    address public immutable factory;

    /// @notice Monotonic replay counter for the direct Falcon-authorized path.
    uint256 public execNonce;

    /// @notice EIP-1271 magic value returned by isValidSignature on success.
    bytes4 internal constant EIP1271_MAGIC = 0x1626ba7e;

    /// @notice ERC-4337 signature-validation-failed sentinel (validationData != 0).
    uint256 internal constant SIG_VALIDATION_FAILED = 1;

    /// @notice NIST Falcon-512 public key: 897 bytes, first byte 0x09 (0x00 | logn=9).
    uint256 internal constant FALCON512_PK_LEN = 897;
    uint8 internal constant FALCON512_PK_HEADER = 0x09;

    /// @notice Signing-domain separators (adversarial-review HIGH, 2026-07-12).
    ///         The message handed to the Falcon verifier for a fund-moving
    ///         authorization (validateUserOp / executeWithFalcon) is
    ///         `abi.encodePacked(domain, hash)`, i.e. 64 bytes, which can NEVER
    ///         collide with the raw 32-byte message that isValidSignature (EIP-1271)
    ///         verifies. Without this separation, a Falcon signature phished through
    ///         an EIP-1271 "login" over a crafted 32-byte hash (set equal to an
    ///         execute-challenge or a userOpHash, both computable from public state
    ///         since execNonce is public) could be replayed as a spend authorization
    ///         and drain the account.
    bytes32 internal constant USEROP_DOMAIN = keccak256("AerePQCAccount.validateUserOp.v1");
    bytes32 internal constant EXEC_DOMAIN = keccak256("AerePQCAccount.executeWithFalcon.v1");

    /// @notice Generic call struct (used by executeBatch).
    struct Call {
        address target;
        uint256 value;
        bytes data;
    }

    /// @notice ERC-4337 v0.7 PackedUserOperation shape (matches AereEntryPointV2).
    struct PackedUserOperation {
        address sender;
        uint256 nonce;
        bytes initCode;
        bytes callData;
        bytes32 accountGasLimits;
        uint256 preVerificationGas;
        bytes32 gasFees;
        bytes paymasterAndData;
        bytes signature;
    }

    event Initialized(address indexed entryPoint, address indexed falcon, uint256 pubKeyLen);
    event Executed(uint256 indexed nonce, address indexed target, uint256 value, bytes data);
    event ExecutedBatch(uint256 indexed nonce, uint256 count);

    error Unauthorized();
    error AlreadyInitialized();
    error NotInitialized();
    error InvalidEntryPoint();
    error InvalidFalconVerifier();
    error InvalidFalconPubKey();
    error InvalidSignature();
    error CallFailed(bytes returnData);

    constructor() {
        factory = msg.sender;
    }

    /// @notice One-shot initializer, callable only by the deploying factory.
    /// @param _entryPoint the ERC-4337 EntryPoint the account will trust.
    /// @param _falcon     the live AereFalcon512Verifier.
    /// @param _falconPubKey the 897-byte NIST Falcon-512 public key that owns this account.
    function initialize(address _entryPoint, address _falcon, bytes calldata _falconPubKey) external {
        if (msg.sender != factory) revert Unauthorized();
        if (entryPoint != address(0)) revert AlreadyInitialized();
        if (_entryPoint == address(0)) revert InvalidEntryPoint();
        if (_falcon == address(0)) revert InvalidFalconVerifier();
        if (_falconPubKey.length != FALCON512_PK_LEN || uint8(_falconPubKey[0]) != FALCON512_PK_HEADER) {
            revert InvalidFalconPubKey();
        }
        entryPoint = _entryPoint;
        falcon = IFalcon512Verifier(_falcon);
        falconPubKey = _falconPubKey;
        emit Initialized(_entryPoint, _falcon, _falconPubKey.length);
    }

    // ─── ERC-4337 path ────────────────────────────────────────────────────────

    /// @notice Called by the EntryPoint to validate a user operation.
    /// @dev    Returns 0 on success and SIG_VALIDATION_FAILED (1) on a bad Falcon
    ///         signature, per ERC-4337 (does not revert on bad signature).
    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
    ) external returns (uint256 validationData) {
        if (msg.sender != entryPoint) revert Unauthorized();

        // Post-quantum authorization: the userOpHash must carry a valid Falcon-512
        // signature under this account's owner key, verified fully on chain. The
        // signed message is domain-separated (USEROP_DOMAIN) so an EIP-1271 login
        // signature over a raw hash can never be replayed here.
        if (!_verifyFalconDomained(USEROP_DOMAIN, userOpHash, userOp.signature)) {
            return SIG_VALIDATION_FAILED;
        }

        // Pay the EntryPoint any prefund it needs (EntryPoint enforces the gas budget).
        if (missingAccountFunds > 0) {
            (bool sent, ) = payable(entryPoint).call{value: missingAccountFunds}("");
            (sent); // ignore — EntryPoint reverts if underfunded
        }
        return 0;
    }

    /// @notice Called by the EntryPoint after validation to dispatch the user's call.
    function executeFromEntryPoint(address target, uint256 value, bytes calldata data) external {
        if (msg.sender != entryPoint) revert Unauthorized();
        (bool ok, bytes memory ret) = target.call{value: value}(data);
        if (!ok) revert CallFailed(ret);
    }

    function executeBatchFromEntryPoint(Call[] calldata calls) external {
        if (msg.sender != entryPoint) revert Unauthorized();
        for (uint256 i = 0; i < calls.length; i++) {
            (bool ok, bytes memory ret) = calls[i].target.call{value: calls[i].value}(calls[i].data);
            if (!ok) revert CallFailed(ret);
        }
    }

    // ─── Direct Falcon-authorized path (self-relay, no bundler required) ───────

    /// @notice Hash the account owner's Falcon key must sign for an executeWithFalcon() call.
    function getExecuteChallenge(
        address target, uint256 value, bytes calldata data, uint256 expectedNonce
    ) public view returns (bytes32) {
        return keccak256(abi.encode(address(this), block.chainid, expectedNonce, target, value, data));
    }

    /// @notice Execute a single call, authorized by a Falcon-512 signature over the
    ///         current execute-challenge. Lets the account be driven directly (e.g.
    ///         by a Foundation relayer) without routing through a 4337 bundler.
    function executeWithFalcon(
        bytes calldata signature,
        address target,
        uint256 value,
        bytes calldata data
    ) external returns (bytes memory result) {
        if (entryPoint == address(0)) revert NotInitialized();
        bytes32 challenge = getExecuteChallenge(target, value, data, execNonce);
        // Domain-separated (EXEC_DOMAIN): a signature phished via the raw-hash
        // isValidSignature surface cannot authorize a spend here.
        if (!_verifyFalconDomained(EXEC_DOMAIN, challenge, signature)) revert InvalidSignature();

        uint256 n = execNonce;
        execNonce = n + 1;

        (bool ok, bytes memory ret) = target.call{value: value}(data);
        if (!ok) revert CallFailed(ret);
        emit Executed(n, target, value, data);
        return ret;
    }

    // ─── EIP-1271 ─────────────────────────────────────────────────────────────

    /// @notice EIP-1271: returns the magic value iff `signature` is a valid
    ///         Falcon-512 signature over `hash` under this account's owner key.
    ///         A `view`, so integrators (Permit2, OpenSea, Snapshot, dapp logins)
    ///         can verify a post-quantum signature for free via eth_call.
    function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
        return _verifyFalcon(hash, signature) ? EIP1271_MAGIC : bytes4(0);
    }

    // ─── Falcon verification (internal) ───────────────────────────────────────

    /// @dev Decode the (nonce, compSig) envelope and run the full on-chain
    ///      Falcon-512 verification of `hash` (as raw 32 bytes) via the live verifier.
    function _verifyFalcon(bytes32 hash, bytes calldata signature) internal view returns (bool) {
        (bytes memory nonce, bytes memory compSig) = abi.decode(signature, (bytes, bytes));
        return falcon.verify(falconPubKey, abi.encodePacked(hash), nonce, compSig);
    }

    /// @dev Domain-separated Falcon verify for the fund-moving paths. The signed
    ///      message is `abi.encodePacked(domain, hash)` (64 bytes), which can never
    ///      equal the raw 32-byte message that isValidSignature verifies, so a
    ///      signature obtained through the EIP-1271 login surface cannot be replayed
    ///      as a validateUserOp or executeWithFalcon spend authorization.
    function _verifyFalconDomained(bytes32 domain, bytes32 hash, bytes calldata signature) internal view returns (bool) {
        (bytes memory nonce, bytes memory compSig) = abi.decode(signature, (bytes, bytes));
        return falcon.verify(falconPubKey, abi.encodePacked(domain, hash), nonce, compSig);
    }

    receive() external payable {}
}
