aere-research/bench/bench.py
Aere Network 4a0b48588c 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:30 +03:00

157 lines
6.7 KiB
Python

#!/usr/bin/env python3
# Falcon-512 verify gas benchmark: WITH vs WITHOUT the native HashToPoint precompile.
# Deploys the stock in-EVM verifier (V0) and the precompile-backed variant (V1) on
# the isolated fork node, runs identical real Falcon-512 KAT vectors through both,
# and reports receipt gasUsed. Also confirms genuine sigs accept / tampered reject
# on BOTH (integration KAT for the precompile).
import json
import subprocess
import sys
from web3 import Web3
from eth_account import Account
RPC = "http://127.0.0.1:8545"
KEY = "0x0000000000000000000000000000000000000000000000000000000000000001"
acct = Account.from_key(KEY)
w3 = Web3(Web3.HTTPProvider(RPC))
# QBFT is proof-of-authority: extraData > 32 bytes, needs the POA middleware.
try:
from web3.middleware import ExtraDataToPOAMiddleware
w3.middleware_onion.inject(ExtraDataToPOAMiddleware, layer=0)
except Exception:
from web3.middleware import geth_poa_middleware # older web3
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
assert w3.is_connected(), "no RPC"
CHAIN = w3.eth.chain_id
def compile_all():
out = subprocess.check_output([
"solc", "--combined-json", "abi,bin", "--optimize", "--optimize-runs", "200",
"/root/staging/bench/AereFalcon512Verifier.sol",
"/root/staging/bench/AereFalcon512VerifierHTP.sol",
])
j = json.loads(out)
res = {}
for name, c in j["contracts"].items():
short = name.split(":")[-1]
res[short] = {"abi": c["abi"] if isinstance(c["abi"], list) else json.loads(c["abi"]), "bin": c["bin"]}
return res
def gas_price():
blk = w3.eth.get_block("latest")
base = blk.get("baseFeePerGas", 0) or 0
return int(base) * 2 + 10 ** 9
def send(tx):
tx.setdefault("chainId", CHAIN)
tx.setdefault("nonce", w3.eth.get_transaction_count(acct.address))
tx.setdefault("gas", 40_000_000)
tx.setdefault("gasPrice", gas_price())
signed = acct.sign_transaction(tx)
raw = getattr(signed, "raw_transaction", None) or signed.rawTransaction
h = w3.eth.send_raw_transaction(raw)
return w3.eth.wait_for_transaction_receipt(h, timeout=120)
def deploy(art):
c = w3.eth.contract(abi=art["abi"], bytecode=art["bin"])
tx = c.constructor().build_transaction({"from": acct.address, "gas": 12_000_000,
"gasPrice": gas_price(), "nonce": w3.eth.get_transaction_count(acct.address),
"chainId": CHAIN})
r = send(tx)
assert r.status == 1, "deploy failed"
return w3.eth.contract(address=r.contractAddress, abi=art["abi"]), r.gasUsed
def parse_sm(sm: bytes):
siglen = (sm[0] << 8) | sm[1]
nonce = sm[2:42]
msglen = len(sm) - 2 - 40 - siglen
message = sm[42:42 + msglen]
return nonce, message
def main():
vec_path = sys.argv[1] if len(sys.argv) > 1 else "/root/staging/kat/falcon512_bench_vectors.txt"
out_path = sys.argv[2] if len(sys.argv) > 2 else "/root/staging/bench/bench-results.json"
# --- precompile liveness sanity ---
sample = (bytes([9]) + b"\x00" * 40 + b"abc")
htp_out = w3.eth.call({"to": "0x0000000000000000000000000000000000000AE7", "data": "0x" + sample.hex()})
assert len(htp_out) == 1024, f"0x0AE7 not active (got {len(htp_out)} bytes)"
print(f"precompile 0x0AE7 live: returned {len(htp_out)} bytes for logn=9")
arts = compile_all()
v0, g0_deploy = deploy(arts["AereFalcon512Verifier"])
v1, g1_deploy = deploy(arts["AereFalcon512VerifierHTP"])
print(f"deployed V0(in-EVM)={v0.address} gas={g0_deploy} V1(precompile)={v1.address} gas={g1_deploy}")
rows = []
for line in open(vec_path):
line = line.strip()
if not line:
continue
tc, pk_hex, sm_hex = line.split("|")
pk = bytes.fromhex(pk_hex)
sm = bytes.fromhex(sm_hex)
nonce, message = parse_sm(sm)
# correctness (eth_call) — genuine must accept on both
ok0 = v0.functions.verifySignedMessage(pk, sm).call()
ok1 = v1.functions.verifySignedMessage(pk, sm).call()
# tampered must reject on both
bad = bytearray(sm); bad[-1] ^= 0x01
bad0 = v0.functions.verifySignedMessage(pk, bytes(bad)).call()
bad1 = v1.functions.verifySignedMessage(pk, bytes(bad)).call()
# gas via state-changing verifyAndRecord (identical calldata both sides)
r0 = send(v0.functions.verifyAndRecord(pk, sm).build_transaction(
{"from": acct.address, "gas": 40_000_000, "gasPrice": gas_price(),
"nonce": w3.eth.get_transaction_count(acct.address), "chainId": CHAIN}))
r1 = send(v1.functions.verifyAndRecord(pk, sm).build_transaction(
{"from": acct.address, "gas": 40_000_000, "gasPrice": gas_price(),
"nonce": w3.eth.get_transaction_count(acct.address), "chainId": CHAIN}))
# component gas: standalone hashToPoint estimate on each
htp0 = v0.functions.hashToPoint(nonce, message).estimate_gas({"from": acct.address})
htp1 = v1.functions.hashToPoint(nonce, message).estimate_gas({"from": acct.address})
row = {"tc": tc, "accept_v0": ok0, "accept_v1": ok1, "reject_tampered_v0": not bad0,
"reject_tampered_v1": not bad1, "verify_gas_in_evm": r0.gasUsed,
"verify_gas_precompile": r1.gasUsed, "verify_gas_drop": r0.gasUsed - r1.gasUsed,
"hashToPoint_gas_in_evm": htp0, "hashToPoint_gas_precompile": htp1}
rows.append(row)
print(f"tc{tc} accept v0={ok0} v1={ok1} tamperReject v0={not bad0} v1={not bad1} "
f"| verify gas {r0.gasUsed} -> {r1.gasUsed} (drop {r0.gasUsed - r1.gasUsed}) "
f"| HTP {htp0} -> {htp1}")
n = len(rows)
avg = lambda k: sum(r[k] for r in rows) // n
all_ok = all(r["accept_v0"] and r["accept_v1"] and r["reject_tampered_v0"] and r["reject_tampered_v1"] for r in rows)
summary = {
"chainId": CHAIN, "vectors": n, "integration_all_pass": all_ok,
"avg_verify_gas_in_evm": avg("verify_gas_in_evm"),
"avg_verify_gas_precompile": avg("verify_gas_precompile"),
"avg_verify_gas_drop": avg("verify_gas_drop"),
"avg_verify_pct_drop": round(100 * avg("verify_gas_drop") / avg("verify_gas_in_evm"), 1),
"avg_hashToPoint_gas_in_evm": avg("hashToPoint_gas_in_evm"),
"avg_hashToPoint_gas_precompile": avg("hashToPoint_gas_precompile"),
"deploy_gas_in_evm": g0_deploy, "deploy_gas_precompile": g1_deploy,
"rows": rows,
}
json.dump(summary, open(out_path, "w"), indent=2)
print("\n=== SUMMARY ===")
print(json.dumps({k: v for k, v in summary.items() if k != "rows"}, indent=2))
if not all_ok:
print("INTEGRATION_FAILED"); sys.exit(3)
print("BENCH_DONE")
if __name__ == "__main__":
main()