Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
- uses: leanprover/lean-action@v1
with:
build-args: "--wfail -v"
- name: Check codegen'd IxVM kernel is up to date
run: lake exe ix codegen --check
- name: Test Ix CLI
run: lake test -- cli
- name: Aiur tests
Expand Down
4 changes: 3 additions & 1 deletion Benchmarks/IxVM.lean
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ def main : IO Unit := do

let _ ← bgroup "IxVM benchmarks" { oneShot := true } do
throughput (.Elements n.toUInt64 "consts")
-- IxVM-native prove: routes execution through the codegen'd Rust
-- kernel (`execute_generated`) instead of the bytecode interpreter.
bench "serde/blake3 Nat.add_comm"
(aiurSystem.prove friParameters funIdx #[.ofNat n])
(aiurSystem.proveIxVM friParameters funIdx #[.ofNat n])
ioBuffer
return
47 changes: 35 additions & 12 deletions Benchmarks/Typecheck.lean
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,6 @@ def runTypecheckCmd (p : Cli.Parsed) : IO UInt32 := do
| .error e => IO.eprintln s!"deserialize {ixePath} failed: {e}"; return 1
| .ok env => pure env
IO.println s!"Loaded {ixePath}: {ixonEnv.namedCount} named, {ixonEnv.constCount} consts"
-- Build the witness for `addr`: subject-only (`verify_const`) trusts deps;
-- otherwise the full-closure check (`verify_claim`, `check addr none`).
let mkWitness (addr : Address) : IO IxVM.ClaimHarness.ClaimWitness :=
if subjectOnly then pure (IxVM.ClaimHarness.buildVerifyConst ixonEnv addr)
else IO.ofExcept (IxVM.ClaimHarness.buildClaimWitness ixonEnv (Ix.Claim.check addr none))
let mut targets : Array (String × Address) := #[]
for arg in nameArgs do
match Ix.Cli.NameResolve.resolveIxeAddr ixonEnv arg with
Expand All @@ -186,16 +181,28 @@ def runTypecheckCmd (p : Cli.Parsed) : IO UInt32 := do
IO.eprintln "no requested constants were found in the env"
return 1

-- Build the env once into a Rust-owned `EnvHandle` and share it
-- across both Phase 1 and Phase 2 loops. Per-target FFI calls
-- reuse the parsed env — no per-call mmap / lazy-index rebuild.
let envHandle ← match Aiur.EnvHandle.fromIxe ixePath with
| .error e => IO.eprintln s!"EnvHandle.fromIxe {ixePath}: {e}"; return 1
| .ok h => pure h

-- Phase 1: execute every constant (cheap, deterministic structural metrics).
-- Carry each target's address through so phase 2 can rebuild its witness.
-- For full-closure check claims, use `checkAddrWithEnv` against the
-- shared `envHandle`. For `--subject-only` (`buildVerifyConst`), the
-- witness is a small subject-only blob — keep Lean witness +
-- `executeIxVM`.
IO.println "── Phase 1: execute (witness generation) ──"
let mut execed : Array (Result × Address) := #[]
for (label, addr) in targets do
try
let witness ← mkWitness addr
let (res, execSec) ← timed fun _ =>
Aiur.Bytecode.Toplevel.execute compiled.bytecode funIdx
witness.input witness.inputIOBuffer
if subjectOnly then
let witness := IxVM.ClaimHarness.buildVerifyConst ixonEnv addr
compiled.bytecode.executeIxVM funIdx witness.input witness.inputIOBuffer
else
compiled.bytecode.checkAddrWithEnv funIdx envHandle addr.hash
match res with
| .error e => IO.eprintln s!" execute {label} failed: {e}"
| .ok (_, _, queryCounts) =>
Expand Down Expand Up @@ -226,9 +233,25 @@ def runTypecheckCmd (p : Cli.Parsed) : IO UInt32 := do
for i in [:ordered.size] do
let (r, addr) := ordered[i]!
try
let witness ← mkWitness addr
let (_, proveSec) ← timed fun _ =>
aiurSystem.prove friParameters funIdx witness.input witness.inputIOBuffer
let (proveRes, proveSec) ← timed fun _ =>
if subjectOnly then
let witness := IxVM.ClaimHarness.buildVerifyConst ixonEnv addr
let (claim, proof, ioBuf) :=
aiurSystem.proveIxVM friParameters funIdx witness.input witness.inputIOBuffer
(.ok (claim, proof, ioBuf) :
Except String (Array Aiur.G × Aiur.Proof × Aiur.IOBuffer))
else
match aiurSystem.proveAddrWithEnv friParameters funIdx envHandle addr.hash with
| .error e => .error e
| .ok (_claimBytes, proof, ioBuf) =>
-- The shared envHandle path doesn't return an `Array G`
-- claim — adapt to the existing benchmark return shape
-- by recomputing the claim digest from the witness's
-- input (Phase 2 doesn't read it).
.ok (#[], proof, ioBuf)
match (proveRes : Except String (Array Aiur.G × Aiur.Proof × Aiur.IOBuffer)) with
| .error e => IO.eprintln s!" prove {r.name} failed: {e}"; continue
| .ok _ => pure ()
spent := spent + proveSec
IO.println s!" {r.name}: prove={proveSec}s (cumulative {spent}s)"
ordered := ordered.set! i ({ r with proveSec := some proveSec }, addr)
Expand Down
61 changes: 59 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"crates/common",
"crates/compile",
"crates/ffi",
"crates/ix",
"crates/ixon",
"crates/kernel",
]
Expand All @@ -27,6 +28,7 @@ license = "MIT OR Apache-2.0"
[workspace.dependencies]
# Internal crates
aiur = { path = "crates/aiur" }
ix = { path = "crates/ix" }
ix-common = { path = "crates/common" }
ix-compile = { path = "crates/compile" }
ixon = { path = "crates/ixon" }
Expand Down
60 changes: 60 additions & 0 deletions Ix/Aiur/Protocol.lean
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,66 @@ def prove (system : @& AiurSystem) (friParameters : @& FriParameters)
let ioMap := ioMap.foldl (fun acc (k, v) => acc.insert k v) ∅
(claim, proof, ⟨ioData, ioMap⟩)

@[extern "rs_aiur_system_prove_ixvm"]
private opaque proveIxVM' : @& AiurSystem → @& FriParameters →
@& Bytecode.FunIdx → @& Array G →
(ioData : @& Array (G × Array G)) →
(ioMap : @& Array ((G × Array G) × IOKeyInfo)) →
Array G × Proof × Array (G × Array G) × Array ((G × Array G) × IOKeyInfo)

/-- IxVM-native prove: same shape as `prove`, but routes execution
through the codegen'd Rust kernel (`execute_generated`) instead
of the bytecode interpreter. The resulting `Proof` is
verification-compatible with one from `prove`. Only valid when
`system.toplevel` is the IxVM kernel's bytecode. -/
def proveIxVM (system : @& AiurSystem) (friParameters : @& FriParameters)
(funIdx : @& Bytecode.FunIdx) (args : @& Array G) (ioBuffer : IOBuffer) :
Array G × Proof × IOBuffer :=
let ioData := ioBuffer.data.toArray
let ioMap := ioBuffer.map.toArray
let (claim, proof, ioData, ioMap) := proveIxVM' system friParameters funIdx args
ioData ioMap
let ioData := ioData.foldl (fun acc (k, v) => acc.insert k v) ∅
let ioMap := ioMap.foldl (fun acc (k, v) => acc.insert k v) ∅
(claim, proof, ⟨ioData, ioMap⟩)

@[extern "rs_aiur_system_prove_addr_with_env"]
private opaque proveAddrWithEnv' : @& AiurSystem → @& FriParameters →
@& Bytecode.FunIdx → @& EnvHandle → @& ByteArray →
Except String (ByteArray × Proof ×
Array (G × Array G) × Array ((G × Array G) × IOKeyInfo))

/-- Per-claim prove against a Rust-owned `EnvHandle`. Returns
`(claimBytes, proof, ioBuffer)` — Rust serializes the
reconstructed `Ix.Claim` via `ixon::Claim::put` so Lean can
deserialize directly without re-running the closure walk. -/
def proveAddrWithEnv (system : @& AiurSystem) (friParameters : @& FriParameters)
(funIdx : @& Bytecode.FunIdx) (envHandle : @& EnvHandle) (addrBytes : ByteArray) :
Except String (ByteArray × Proof × IOBuffer) :=
match proveAddrWithEnv' system friParameters funIdx envHandle addrBytes with
| .error e => .error e
| .ok (claimBytes, proof, ioData, ioMap) =>
let ioData := ioData.foldl (fun acc (k, v) => acc.insert k v) ∅
let ioMap := ioMap.foldl (fun acc (k, v) => acc.insert k v) ∅
.ok (claimBytes, proof, ⟨ioData, ioMap⟩)

@[extern "rs_aiur_system_shard_prove_with_env"]
private opaque shardProveWithEnv' : @& AiurSystem → @& FriParameters →
@& Bytecode.FunIdx → @& EnvHandle → @& ByteArray →
Except String (ByteArray × Proof ×
Array (G × Array G) × Array ((G × Array G) × IOKeyInfo))

/-- Per-shard prove against a Rust-owned `EnvHandle`. -/
def shardProveWithEnv (system : @& AiurSystem) (friParameters : @& FriParameters)
(funIdx : @& Bytecode.FunIdx) (envHandle : @& EnvHandle) (ownedBlob : ByteArray) :
Except String (ByteArray × Proof × IOBuffer) :=
match shardProveWithEnv' system friParameters funIdx envHandle ownedBlob with
| .error e => .error e
| .ok (claimBytes, proof, ioData, ioMap) =>
let ioData := ioData.foldl (fun acc (k, v) => acc.insert k v) ∅
let ioMap := ioMap.foldl (fun acc (k, v) => acc.insert k v) ∅
.ok (claimBytes, proof, ⟨ioData, ioMap⟩)

@[extern "rs_aiur_system_verify"]
opaque verify : @& AiurSystem → @& FriParameters →
@& Array G → @& Proof → Except String Unit
Expand Down
Loading