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.
220 lines
8.4 KiB
Solidity
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);
|
|
}
|
|
}
|