aere-contracts/contracts/AereCancunCanary.sol
Aere Network a13a649b77
Some checks are pending
contracts-ci / Install (lockfile) → compile → full test suite (push) Waiting to run
contracts-ci / PQC known-answer tests (NIST vectors) (push) Waiting to run
contracts-ci / Coverage (scoped, with artifacts) (push) Waiting to run
Initial public release
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.
2026-07-20 01:02:37 +03:00

220 lines
8.4 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
/**
* @title AereCancunCanary
* @notice On-chain canary proving Cancun EVM support on AERE (chain 2800):
* - TSTORE / TLOAD (EIP-1153), including auto-reset between
* transactions and a transient reentrancy lock,
* - MCOPY (EIP-5656) round-trip verified by keccak256,
* - PUSH0 (EIP-3855), implicit in solc >=0.8.20 shanghai+ output,
* - BLOBHASH (EIP-4844) and BLOBBASEFEE (EIP-7516) opcode execution.
*
* Compiled with solc 0.8.24, evmVersion = cancun, via a per-file
* hardhat override (the repo global stays 0.8.23 / shanghai-default).
*
* The constructor self-tests every opcode, so a successful deploy,
* or even a read-only eth_estimateGas / eth_call of the creation
* bytecode, is itself proof that the live chain executes Cancun
* opcodes: any TSTORE/TLOAD/MCOPY/BLOBHASH/BLOBBASEFEE failure
* reverts deployment.
*
* PUSH0 note for verifiers: the deployed runtime bytecode contains
* genuine PUSH0 (0x5f) opcodes; the canonical solc revert stub
* `5f80fd` (PUSH0 DUP1 REVERT) appears in the runtime code. To check
* rigorously, walk the bytecode from offset 0 skipping PUSH1..PUSH32
* immediates and confirm 0x5f at an opcode position. Every
* successful call to this contract executes PUSH0 on-chain.
*
* Blob note: AERE is a QBFT L1 that does not accept blob (type-3)
* transactions, so blobhash(0) == bytes32(0) in every execution
* context. blobbasefee is whatever the node's Cancun fee market
* reports (protocol minimum is 1 wei); it is recorded, not asserted.
*/
contract AereCancunCanary {
/// @dev keccak256("aere.cancun.canary.tslot"); literal so assembly can use it.
uint256 private constant T_SLOT =
0x600b3c753bdea90810db30bcdc9015ebc00429a1e131fa35c272b23785c573b1;
/// @dev keccak256("aere.cancun.canary.lock"); literal so assembly can use it.
uint256 private constant LOCK_SLOT =
0xe4d0d997dd2753c27700e5fe7c65208afc32dd89178d805290c63adf8ca6331e;
/// @notice true iff the constructor's TSTORE/TLOAD + MCOPY + blob-opcode
/// self-test passed on the deploying chain.
bool public immutable deploySelfTest;
event TransientWithinTx(uint256 written, uint256 readBack);
event TransientCrossTx(uint256 observed);
event ReentrancyLockProbe(bool attemptedReentry, bool innerCallReverted);
event McopyChecked(uint256 dataLen, bytes32 inputHash, bytes32 copyHash, bool ok);
event BlobProbed(bytes32 blobHash0, uint256 blobBaseFee);
error TransientLockActive();
/// @dev Reentrancy guard using EIP-1153 transient storage. Costs no
/// cold-SSTORE gas and needs no post-tx cleanup guarantees beyond
/// the EVM's own end-of-transaction transient reset.
modifier transientLock() {
uint256 lockV;
assembly {
lockV := tload(LOCK_SLOT)
}
if (lockV != 0) revert TransientLockActive();
assembly {
tstore(LOCK_SLOT, 1)
}
_;
assembly {
tstore(LOCK_SLOT, 0)
}
}
constructor() {
// EIP-1153 round-trip.
uint256 v;
assembly {
tstore(T_SLOT, 0xCA9A17)
v := tload(T_SLOT)
}
require(v == 0xCA9A17, "TSTORE/TLOAD failed");
assembly {
tstore(T_SLOT, 0)
}
// EIP-5656 round-trip.
bytes memory src = abi.encodePacked(
keccak256("aere.cancun.canary.mcopy.src"),
uint256(2800)
);
bytes memory dst = new bytes(src.length);
uint256 len = src.length;
assembly {
mcopy(add(dst, 0x20), add(src, 0x20), len)
}
require(keccak256(dst) == keccak256(src), "MCOPY failed");
// EIP-4844 / EIP-7516 opcodes must execute (no blob txs on QBFT, so
// blobhash(0) is zero; blobbasefee is recorded, not asserted).
bytes32 h;
uint256 f;
assembly {
h := blobhash(0)
f := blobbasefee()
}
require(h == bytes32(0), "unexpected blob hash");
emit BlobProbed(h, f);
deploySelfTest = true;
}
// ---------------------------------------------------------------------
// EIP-1153 transient storage
// ---------------------------------------------------------------------
/// @notice Method A: TSTOREs `value` and TLOADs it back within the same
/// transaction; emits both as proof.
function transientWriteAndRead(uint256 value) external returns (uint256 readBack) {
assembly {
tstore(T_SLOT, value)
readBack := tload(T_SLOT)
}
emit TransientWithinTx(value, readBack);
}
/// @notice Method B: TLOADs the same slot in a LATER transaction. Must
/// observe 0, proving the EVM auto-resets transient storage
/// between transactions.
function transientReadOnly() external returns (uint256 observed) {
assembly {
observed := tload(T_SLOT)
}
emit TransientCrossTx(observed);
}
/// @notice View twin of transientReadOnly for eth_call checks.
function transientPeek() external view returns (uint256 observed) {
assembly {
observed := tload(T_SLOT)
}
}
/// @notice Current value of the transient reentrancy lock slot (always 0
/// when read from a fresh transaction).
function lockPeek() external view returns (uint256 v) {
assembly {
v := tload(LOCK_SLOT)
}
}
/// @notice Transient reentrancy lock demonstration. With
/// `attemptReentry = true` the function re-enters itself via an
/// external self-call; the inner call MUST revert with
/// TransientLockActive, and `innerCallReverted` is emitted true.
/// A subsequent plain call (`attemptReentry = false`) succeeds,
/// showing the lock cleared.
function guardedEnter(bool attemptReentry)
external
transientLock
returns (bool innerCallReverted)
{
if (attemptReentry) {
(bool ok, ) = address(this).call(
abi.encodeWithSelector(this.guardedEnter.selector, false)
);
innerCallReverted = !ok;
}
emit ReentrancyLockProbe(attemptReentry, innerCallReverted);
}
// ---------------------------------------------------------------------
// EIP-5656 MCOPY
// ---------------------------------------------------------------------
/// @notice Copies `data` with assembly `mcopy` and returns keccak256 of
/// both the input and the copy; equal hashes prove a faithful
/// MCOPY round-trip.
function mcopyHash(bytes calldata data)
public
pure
returns (bytes32 inputHash, bytes32 copyHash)
{
bytes memory input = data;
inputHash = keccak256(input);
uint256 len = input.length;
bytes memory copyBuf = new bytes(len);
assembly {
mcopy(add(copyBuf, 0x20), add(input, 0x20), len)
copyHash := keccak256(add(copyBuf, 0x20), len)
}
}
/// @notice Transaction variant of mcopyHash; emits the comparison so the
/// proof lives in a receipt.
function mcopyCheck(bytes calldata data) external returns (bool ok) {
(bytes32 inputHash, bytes32 copyHash) = mcopyHash(data);
ok = inputHash == copyHash;
emit McopyChecked(data.length, inputHash, copyHash, ok);
}
// ---------------------------------------------------------------------
// EIP-4844 BLOBHASH / EIP-7516 BLOBBASEFEE
// ---------------------------------------------------------------------
/// @notice Returns blobhash(0) and blobbasefee via assembly. On AERE
/// (QBFT, no blob transactions) blobhash(0) == bytes32(0);
/// blobbasefee is the node-reported blob base fee (min 1 wei).
function blobView() public view returns (bytes32 blobHash0, uint256 blobBaseFee) {
assembly {
blobHash0 := blobhash(0)
blobBaseFee := blobbasefee()
}
}
/// @notice Transaction variant of blobView; emits the values.
function blobProbe() external returns (bytes32 blobHash0, uint256 blobBaseFee) {
(blobHash0, blobBaseFee) = blobView();
emit BlobProbed(blobHash0, blobBaseFee);
}
}