Aere Network public source. Everything here can be checked against the live chain (chain id 2800, https://rpc.aere.network). Scope note, stated up front rather than buried: consensus on chain 2800 is classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at the signature, precompile, account and transport layers. Nothing here makes the consensus post-quantum, and no document in it should be read as claiming so.
80 lines
3.7 KiB
Solidity
80 lines
3.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.23;
|
|
|
|
import {AerePQCAccount} from "./AerePQCAccount.sol";
|
|
|
|
/**
|
|
* @title AerePQCAccountFactory, CREATE2 factory for post-quantum smart accounts (roadmap #270)
|
|
* @notice Deterministically deploys AerePQCAccount instances keyed on a NIST
|
|
* Falcon-512 public key (897 bytes) plus a salt, mirroring
|
|
* AerePasskeyAccountFactoryV2. The account address is a pure function of
|
|
* (falconPubKey, salt): the same Falcon key with the same salt always
|
|
* maps to the same address, so an account can be funded at its
|
|
* counterfactual address before it is deployed, and re-deploying is a
|
|
* no-op that returns the existing account.
|
|
*
|
|
* @dev All accounts from one factory share the factory's EntryPoint and
|
|
* Falcon-512 verifier (both immutable, set at factory deployment), so the
|
|
* account creationCode carries no constructor args and the CREATE2 init
|
|
* code hash is a constant.
|
|
*/
|
|
contract AerePQCAccountFactory {
|
|
/// @notice keccak256 of the AerePQCAccount creation bytecode (no constructor args).
|
|
bytes32 public constant ACCOUNT_INIT_CODE_HASH = keccak256(type(AerePQCAccount).creationCode);
|
|
|
|
/// @notice NIST Falcon-512 public key length + header (0x00 | logn=9).
|
|
uint256 internal constant FALCON512_PK_LEN = 897;
|
|
uint8 internal constant FALCON512_PK_HEADER = 0x09;
|
|
|
|
/// @notice The EntryPoint every account created by this factory trusts.
|
|
address public immutable defaultEntryPoint;
|
|
|
|
/// @notice The live AereFalcon512Verifier every account delegates verification to.
|
|
address public immutable falconVerifier;
|
|
|
|
event AccountCreated(address indexed account, address indexed entryPoint, bytes falconPubKey, uint256 salt);
|
|
|
|
error InvalidEntryPoint();
|
|
error InvalidFalconVerifier();
|
|
error InvalidFalconPubKey();
|
|
error AddressMismatch();
|
|
|
|
constructor(address _defaultEntryPoint, address _falconVerifier) {
|
|
if (_defaultEntryPoint == address(0)) revert InvalidEntryPoint();
|
|
if (_falconVerifier == address(0)) revert InvalidFalconVerifier();
|
|
defaultEntryPoint = _defaultEntryPoint;
|
|
falconVerifier = _falconVerifier;
|
|
}
|
|
|
|
/// @notice Deterministic account address for (falconPubKey, salt).
|
|
function predictAddress(bytes calldata falconPubKey, uint256 salt) public view returns (address) {
|
|
bytes32 saltMix = _saltMix(falconPubKey, salt);
|
|
return address(uint160(uint256(keccak256(abi.encodePacked(
|
|
bytes1(0xff), address(this), saltMix, ACCOUNT_INIT_CODE_HASH
|
|
)))));
|
|
}
|
|
|
|
/// @notice Idempotent: deploys + initialises the account for `falconPubKey`, or
|
|
/// returns the already-deployed account at the same address.
|
|
function createAccount(bytes calldata falconPubKey, uint256 salt)
|
|
external returns (AerePQCAccount account)
|
|
{
|
|
if (falconPubKey.length != FALCON512_PK_LEN || uint8(falconPubKey[0]) != FALCON512_PK_HEADER) {
|
|
revert InvalidFalconPubKey();
|
|
}
|
|
address predicted = predictAddress(falconPubKey, salt);
|
|
if (predicted.code.length > 0) {
|
|
return AerePQCAccount(payable(predicted));
|
|
}
|
|
bytes32 saltMix = _saltMix(falconPubKey, salt);
|
|
account = new AerePQCAccount{salt: saltMix}();
|
|
account.initialize(defaultEntryPoint, falconVerifier, falconPubKey);
|
|
if (address(account) != predicted) revert AddressMismatch();
|
|
emit AccountCreated(address(account), defaultEntryPoint, falconPubKey, salt);
|
|
}
|
|
|
|
function _saltMix(bytes calldata falconPubKey, uint256 salt) internal pure returns (bytes32) {
|
|
return keccak256(abi.encode(falconPubKey, salt));
|
|
}
|
|
}
|