Skip to content

Commit 7dd9291

Browse files
starknet_os_flow_tests: add repro test for privacy-prove keccak builtin failure
Adds an `#[ignore]`-gated test, `prove_and_verify_keccak_tx`, that deterministically reproduces a `privacy-prove` proving failure when a virtual OS PIE contains keccak builtin usage from a user contract. OS execution succeeds; the failure is in the privacy bootloader's `load_cairo_pie` hint relocating the inner PIE memory. See REPRO_KECCAK_PRIVACY_PROVE.md for run instructions, observed failure output, suspected root cause (`stwo_no_ecop` layout excludes keccak), and the isolation experiment confirming the failure is triggered by the keccak BUILTIN, not the keccak SYSCALL. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent de06d14 commit 7dd9291

4 files changed

Lines changed: 147 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

REPRO_KECCAK_PRIVACY_PROVE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Repro: privacy-prove fails on virtual OS PIE that uses keccak builtin
2+
3+
This branch carries an `#[ignore]`-gated integration test that demonstrates a
4+
deterministic proving failure in `privacy-prove` when the inner task (a Starknet
5+
virtual OS run) contains keccak builtin usage by a user contract.
6+
7+
The keccak **syscall** in the virtual OS is **not** the cause — that path proves
8+
fine. The failure is triggered by the keccak **builtin** appearing in the OS PIE.
9+
10+
## Test
11+
12+
`crates/starknet_os_flow_tests/src/virtual_os_test.rs::prove_and_verify_keccak_tx`
13+
14+
It invokes `test_contract::test_keccak`, which exercises both the keccak builtin
15+
(`keccak::keccak_u256s_le_inputs`) and the keccak syscall (failure path with an
16+
invalid length).
17+
18+
## How to run
19+
20+
Requires the project's Python venv (for the Cairo 0 compiler) and the
21+
nightly Rust toolchain that the prover crate needs.
22+
23+
```bash
24+
# From the repo root:
25+
source sequencer_venv/bin/activate
26+
27+
cargo +nightly-2025-07-14 test \
28+
-p starknet_os_flow_tests \
29+
--features starknet_transaction_prover/stwo_proving \
30+
--release \
31+
prove_and_verify_keccak_tx \
32+
-- --ignored --nocapture
33+
```
34+
35+
First run cold-builds the workspace in release mode (~5 min on a fast
36+
machine); subsequent runs are fast. The test itself runs the OS, proves, and
37+
verifies in ~40 s once built.
38+
39+
## Observed failure
40+
41+
```
42+
thread 'virtual_os_test::prove_and_verify_keccak_tx' panicked at
43+
crates/starknet_os_flow_tests/src/virtual_os_test_manager.rs:42:53:
44+
Proving virtual OS should not fail.:
45+
ProvingError(ProverExecution(
46+
".../simple_bootloader/execute_task.cairo:209:5: Error at pc=0:615:
47+
Got an exception while executing a hint: Hint Error:
48+
Error while relocating Cairo PIE memory: Memory addresses must be relocatable
49+
Cairo traceback (most recent call last):
50+
<start>:3:1: (pc=0:2)
51+
.../simple_bootloader/privacy_simple_bootloader.cairo:109:5: (pc=0:1116)
52+
.../simple_bootloader/run_simple_bootloader.cairo:114:9: (pc=0:727)
53+
.../simple_bootloader/run_simple_bootloader.cairo:199:5: (pc=0:782)"
54+
))
55+
```
56+
57+
OS execution succeeds — the failure is strictly in the proving step.
58+
59+
## Suspected root cause
60+
61+
1. `privacy_prove::consts::CAIRO_RUN_CONFIG` (`crates/privacy_prove/src/consts.rs`
62+
in `proving-utils` rev `580135e`) selects `LayoutName::stwo_no_ecop`.
63+
2. `BuiltinsInstanceDef::stwo_no_ecop()` in `cairo-vm-3.2.0` returns
64+
`keccak: None, ecdsa: None, ec_op: None`.
65+
3. The privacy bootloader's `simple_bootloader_simulate_keccak` hint installs an
66+
auto-deduction-rule-based simulated keccak segment, but the relocation logic
67+
in
68+
`cairo-program-runner-lib::hints::load_cairo_pie::build_cairo_pie_relocation_table`
69+
reads `cairo_pie_execution_segment[idx]` for each declared builtin and
70+
`extract_segment`s a relocatable from it. When the inner OS PIE has actually
71+
exercised keccak (so the keccak builtin segment in the PIE is non-empty), the
72+
value found at that cell is a felt (Int), not a relocatable, and
73+
`extract_segment` returns `MemoryError::AddressNotRelocatable`.
74+
75+
## Isolation experiment (already verified locally)
76+
77+
Comment out the keccak BUILTIN call inside
78+
`crates/blockifier_test_utils/resources/feature_contracts/cairo1/test_contract.cairo::test_keccak`
79+
(the `keccak::keccak_u256s_le_inputs(...)` block, lines ~642–647 on this branch),
80+
leaving only the keccak SYSCALL failure-path call. Re-run the same command — the
81+
test now proves and verifies cleanly. This confirms the failure is exercised by
82+
the keccak BUILTIN, not the SYSCALL.
83+
84+
## Baselines that pass
85+
86+
- `cargo +nightly-2025-07-14 test -p starknet_os_flow_tests --features starknet_transaction_prover/stwo_proving --release generate_proof_fixtures -- --ignored`
87+
(fund-account tx, no syscalls)
88+
- `prove_and_verify_keccak_tx` modified to call `test_storage_read` instead of
89+
`test_keccak` (non-keccak syscall)
90+
91+
Both succeed on this branch, confirming the proving path itself works and only
92+
the keccak BUILTIN trip path is affected.

crates/starknet_os_flow_tests/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ starknet_committer = { workspace = true, features = ["testing"] }
3434
starknet_os = { workspace = true, features = ["include_program_output", "testing"] }
3535
starknet_patricia = { workspace = true, features = ["testing"] }
3636
starknet_patricia_storage = { workspace = true, features = ["testing"] }
37+
starknet_proof_verifier.workspace = true
3738
starknet_transaction_prover.workspace = true
3839
strum.workspace = true
3940
tokio.workspace = true

crates/starknet_os_flow_tests/src/virtual_os_test.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,59 @@ async fn test_reverted_tx_os_error() {
273273
.run_virtual_expect_error("Reverted transactions are not supported in virtual OS mode");
274274
}
275275

276+
/// Reproduces a privacy-prove failure when proving a virtual OS run that uses the keccak builtin.
277+
///
278+
/// Scenario:
279+
/// - Invoke `test_contract::test_keccak`, which uses both `keccak::keccak_u256s_le_inputs`
280+
/// (keccak BUILTIN) and `syscalls::keccak_syscall` with invalid input length (keccak SYSCALL
281+
/// failure path).
282+
/// - Run through the virtual OS (succeeds), then prove via `privacy-prove` and verify.
283+
///
284+
/// Observed result: OS execution succeeds; proving panics in
285+
/// `cairo-program-runner-lib::hints::load_cairo_pie::extract_segment` with
286+
/// `MemoryError::AddressNotRelocatable`, surfaced as
287+
/// "Hint Error: Error while relocating Cairo PIE memory: Memory addresses must be relocatable"
288+
/// at `simple_bootloader/execute_task.cairo:209`.
289+
///
290+
/// Likely cause: `privacy_prove::consts::CAIRO_RUN_CONFIG` selects `LayoutName::stwo_no_ecop`,
291+
/// which has `keccak: None` (`cairo-vm-3.2.0/src/types/instance_definitions/builtins_instance_def.rs`).
292+
/// The bootloader simulates keccak via `simple_bootloader_simulate_keccak`, but the simulation
293+
/// does not appear to cover relocating an inner task PIE whose keccak builtin segment is
294+
/// non-empty.
295+
///
296+
/// Isolation experiment: commenting out the `keccak::keccak_u256s_le_inputs` call in
297+
/// `test_contract::test_keccak` (leaving only the keccak SYSCALL failure path) makes this test
298+
/// pass — confirming the failure is triggered by keccak BUILTIN usage by the inner contract,
299+
/// not by the keccak SYSCALL itself.
300+
///
301+
/// To run manually: `cargo +nightly-2025-07-14 test -p starknet_os_flow_tests --features
302+
/// starknet_transaction_prover/stwo_proving --release prove_and_verify_keccak_tx -- --ignored
303+
/// --nocapture`
304+
#[tokio::test(flavor = "multi_thread")]
305+
#[ignore]
306+
async fn prove_and_verify_keccak_tx() {
307+
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
308+
309+
let (mut test_builder, [contract_address]) =
310+
TestBuilder::create_standard_virtual([(test_contract, calldata![Felt::ONE, Felt::TWO])])
311+
.await;
312+
313+
let calldata = create_calldata(contract_address, "test_keccak", &[]);
314+
test_builder.add_funded_account_invoke(invoke_tx_args! { calldata });
315+
316+
let test_runner = test_builder.build().await;
317+
let output = test_runner.run_virtual().prove().await;
318+
319+
let proof_facts = output.proof_facts.clone();
320+
let proof = output.proof.clone();
321+
tokio::task::spawn_blocking(move || {
322+
starknet_proof_verifier::verify_proof(proof_facts, proof)
323+
})
324+
.await
325+
.expect("proof verification task panicked")
326+
.expect("proof verification should succeed");
327+
}
328+
276329
/// Generates proof fixtures for the proof-flow integration test.
277330
/// To run manually: `cargo +nightly-2025-07-14 test -p starknet_os_flow_tests --features
278331
/// starknet_transaction_prover/stwo_proving --release generate_proof_fixtures -- --ignored`

0 commit comments

Comments
 (0)