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.
89 lines
4.6 KiB
Solidity
89 lines
4.6 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity 0.8.23;
|
|
|
|
/// @title AereOutboundOutbox
|
|
/// @notice AERE-side (chain 2800) source of the canonical OUTBOUND cross-chain
|
|
/// message. It emits exactly the log the `zk-interop` SP1 guest proves
|
|
/// inclusion of ("event R included in a QBFT-final AERE block"), so a
|
|
/// destination-chain `AereOutboundVerifier` can admit the message with
|
|
/// no bridge multisig — only a zk proof against AERE's finality vkey.
|
|
///
|
|
/// @dev CANONICAL MESSAGE BINDING (must stay byte-identical to the guest).
|
|
/// The guest's `interop_core::ReceiptInclusion::log_commitment(0)` commits:
|
|
///
|
|
/// commitment =
|
|
/// keccak256(
|
|
/// cid(32) // AERE chainId, left-padded
|
|
/// ‖ blockHash(32) // the QBFT-final block that carries the log
|
|
/// ‖ addr(32) // this Outbox address, left-padded
|
|
/// ‖ topic0(32) // keccak256(event signature)
|
|
/// ‖ topic1(32) // messageId (indexed)
|
|
/// ‖ topic2(32) // destChainId (indexed)
|
|
/// ‖ keccak256(data) // keccak(abi.encode(nonce,sender,handler,payload))
|
|
/// )
|
|
///
|
|
/// The verifier re-derives this from the delivered fields + the proof's
|
|
/// committed `blockHash`, and requires it to equal the proof commitment.
|
|
/// That is what binds "the message you are delivering" to "the log AERE's
|
|
/// validators finalised". If you change this event's shape, `messageId`,
|
|
/// or the field order, you MUST update the guest and the verifier together.
|
|
///
|
|
/// @dev LOG-INDEX-0 CONSTRAINT (honest integration limitation). The guest's
|
|
/// `verify_interop` commits `log_commitment(0)` — the FIRST log of the
|
|
/// proven receipt. So the prover must prove a receipt whose log[0] is this
|
|
/// `AereCrossChainMessage`. The safe pattern is a direct EOA -> Outbox
|
|
/// `send` call (receipt has exactly one log, at index 0). If `send` is
|
|
/// invoked as a sub-call after other logs in the same transaction, that
|
|
/// message is NOT at log index 0 and cannot be delivered with the current
|
|
/// guest/verifier. Extending to an arbitrary log index needs a matching
|
|
/// change in both the guest and `AereOutboundVerifier`.
|
|
///
|
|
/// SECURITY MODEL — read before quoting anywhere.
|
|
/// Delivery security equals a `>= quorum` of AERE's 5 QBFT validators sealing
|
|
/// the source block. Those seals are classical ECDSA today (quantum-vulnerable;
|
|
/// a Falcon seal slot is reserved but NOT active — mainnet consensus is not
|
|
/// post-quantum). The destination-side SP1 Groth16 wrapper is BN254, also
|
|
/// quantum-vulnerable. This is TRUST-MINIMISED, not trustless: it removes the
|
|
/// bridge multisig, it does not remove trust in AERE's validator set.
|
|
contract AereOutboundOutbox {
|
|
/// @notice AERE mainnet chain id. Bound into every message commitment.
|
|
uint256 public constant AERE_CHAIN_ID = 2800;
|
|
|
|
/// @notice Monotonic per-outbox message counter. `(AERE_CHAIN_ID, nonce)` is
|
|
/// unique, so no two distinct messages share a `messageId`.
|
|
uint256 public nonce;
|
|
|
|
/// @notice The canonical cross-chain message the guest proves inclusion of.
|
|
/// @dev Signature string (topic0 preimage):
|
|
/// "AereCrossChainMessage(bytes32,uint256,uint256,address,address,bytes)"
|
|
event AereCrossChainMessage(
|
|
bytes32 indexed messageId,
|
|
uint256 indexed destChainId,
|
|
uint256 nonce,
|
|
address sender,
|
|
address targetHandler,
|
|
bytes payload
|
|
);
|
|
|
|
/// @notice Emit an outbound message for `destChainId`, to be delivered to
|
|
/// `targetHandler` on that chain by an `AereOutboundVerifier`.
|
|
/// @param destChainId the destination chain id (bound into `messageId`).
|
|
/// @param targetHandler the handler contract on the destination chain.
|
|
/// @param payload opaque application bytes handed to the target handler.
|
|
/// @return messageId content commitment / replay key on the destination.
|
|
function send(uint256 destChainId, address targetHandler, bytes calldata payload)
|
|
external
|
|
returns (bytes32 messageId)
|
|
{
|
|
require(targetHandler != address(0), "outbox: zero handler");
|
|
uint256 n = nonce;
|
|
nonce = n + 1;
|
|
// messageId binds source chain, destination chain, nonce, sender and
|
|
// the full payload. The verifier re-computes it identically.
|
|
messageId = keccak256(
|
|
abi.encode(AERE_CHAIN_ID, destChainId, n, msg.sender, targetHandler, payload)
|
|
);
|
|
emit AereCrossChainMessage(messageId, destChainId, n, msg.sender, targetHandler, payload);
|
|
}
|
|
}
|