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.
157 lines
6.7 KiB
Python
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()
|