Skip to content

Commit c5bf570

Browse files
starknet_os_flow_tests: add multicall prove test; drop balance_of test
- Add `multi_call(Array<Call>)` to `account_with_dummy_validate.cairo` with a TODO above `__execute__` to eventually replace its single-call shape with the multicall-style entry point. - Add `prove_and_verify_multicall_tx` in `crates/starknet_os_flow_tests/ src/virtual_os_test.rs`: a single virtual-OS tx forwards through `__execute__` to the new `multi_call`, currently dispatching `test_keccak` and `test_ec_op`. Inner calls are appended via a local `serialize_call(name, args)` helper that bumps the leading `num_calls` counter in `multi_call_args`, so each call site is one line. Marked `#[ignore]`; doc carries a TODO to extend coverage. - Remove `test_prove_balance_of_transaction` from `crates/ starknet_transaction_prover/src/proving/virtual_snos_prover_test.rs` — the remaining `test_prove_transfer_transaction` already covers the same `VirtualSnosProver::prove_transaction` Sepolia path. - Run the new test in the prover CI workflow alongside `virtual_snos_prover_test`. Also widen the workflow's path triggers to include `apollo_starknet_os_program/**` (compiles the OS program the prover runs), `blockifier_test_utils/**` (account/contract test sources), and `starknet_os_flow_tests/**`. Path triggers are not transitive, so these had to be listed explicitly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 7dd9291 commit c5bf570

6 files changed

Lines changed: 89 additions & 54 deletions

File tree

.github/workflows/starknet_transaction_prover_ci.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,14 @@ on:
1212
- ".github/workflows/starknet_transaction_prover_ci.yml"
1313
- "Cargo.lock"
1414
- "Cargo.toml"
15+
- "crates/apollo_starknet_os_program/**"
1516
- "crates/blockifier/**"
1617
- "crates/blockifier_reexecution/**"
18+
- "crates/blockifier_test_utils/**"
1719
- "crates/proving_utils/**"
1820
- "crates/starknet_committer/**"
1921
- "crates/starknet_os/**"
22+
- "crates/starknet_os_flow_tests/**"
2023
- "crates/starknet_patricia/**"
2124
- "crates/starknet_transaction_prover/**"
2225
- "scripts/build_starknet_transaction_prover.sh"
@@ -110,6 +113,10 @@ jobs:
110113
working-directory: crates/starknet_transaction_prover
111114
run: cargo test --release -p starknet_transaction_prover --features stwo_proving virtual_snos_prover_test -- --ignored --test-threads=1
112115

116+
- name: Run virtual OS multicall proving test
117+
working-directory: crates/starknet_transaction_prover
118+
run: cargo test --release -p starknet_os_flow_tests --features starknet_transaction_prover/stwo_proving prove_and_verify_multicall_tx -- --ignored --test-threads=1
119+
113120
# Test that the starknet_transaction_prover Docker image builds successfully.
114121
docker-build:
115122
runs-on: namespace-profile-medium-ubuntu-24-04-amd64

crates/blockifier/src/bouncer_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,11 +804,11 @@ fn class_hash_migration_data_from_state(
804804

805805
if should_migrate {
806806
expect![[r#"
807-
108525874
807+
110673392
808808
"#]]
809809
.assert_debug_eq(&migration_sierra_gas.0);
810810
expect![[r#"
811-
266554526
811+
271891610
812812
"#]]
813813
.assert_debug_eq(&migration_proving_gas.0);
814814
} else {

crates/blockifier/src/execution/stack_trace_regression/test_contract_ctor_frame_stack_trace_cairo1_casm.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Transaction execution has failed:
22
0: Error in the called contract (contract address: 0x00000000000000000000000000000000000000000000000000000000c0020000, class hash: 0x0000000000000000000000000000000000000000000000000000000080020000, selector: 0x015d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad):
33
Error at pc=0:539:
44
1: Error in the called contract (contract address: 0x00000000000000000000000000000000000000000000000000000000c0020000, class hash: 0x0000000000000000000000000000000000000000000000000000000080020000, selector: 0x02730079d734ee55315f4f141eaed376bddd8c2133523d223a344c5604e0f7f8):
5-
Error at pc=0:781:
5+
Error at pc=0:950:
66
2: Error in the contract class constructor (contract address: 0x0103ee82605273496eed8d9141c5b3ad967baa08be63aa5bc49ffae5eae454cc, class hash: 0x0000000000000000000000000000000000000000000000000000000080040000, selector: 0x028ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194):
77
Execution failed. Failure reason:
88
Error in contract (contract address: 0x0103ee82605273496eed8d9141c5b3ad967baa08be63aa5bc49ffae5eae454cc, class hash: 0x0000000000000000000000000000000000000000000000000000000080040000, selector: 0x028ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194):

crates/blockifier_test_utils/resources/feature_contracts/cairo1/account_with_dummy_validate.cairo

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#[starknet::contract(account)]
22
mod Account {
33
use array::{ArrayTrait, SpanTrait};
4+
use starknet::account::Call;
45
use starknet::{ClassHash, ContractAddress, call_contract_syscall};
56
use starknet::info::SyscallResultTrait;
67
use starknet::syscalls;
@@ -34,6 +35,9 @@ mod Account {
3435
starknet::VALIDATED
3536
}
3637

38+
// TODO(Yoni): replace this single-call `__execute__` with the multicall-shaped
39+
// `multi_call` below, so this account can execute INVOKE transactions whose
40+
// calldata is `Array<Call>` natively (matching the standard account ABI).
3741
#[external(v0)]
3842
#[raw_output]
3943
fn __execute__(
@@ -52,6 +56,32 @@ mod Account {
5256
).unwrap_syscall()
5357
}
5458

59+
/// Executes a sequence of calls and returns their concatenated return values.
60+
/// The intended invocation in tests is via `__execute__`, with `__execute__`
61+
/// forwarding `(self_address, "multi_call", serialized calls)` to this entry point.
62+
#[external(v0)]
63+
fn multi_call(
64+
self: @ContractState, mut calls: Array<Call>
65+
) -> Array<Span<felt252>> {
66+
let mut result = ArrayTrait::new();
67+
loop {
68+
match calls.pop_front() {
69+
Option::Some(call) => {
70+
let res = call_contract_syscall(
71+
address: call.to,
72+
entry_point_selector: call.selector,
73+
calldata: call.calldata,
74+
).unwrap_syscall();
75+
result.append(res);
76+
},
77+
Option::None => {
78+
break;
79+
},
80+
};
81+
};
82+
result
83+
}
84+
5585
#[external(v0)]
5686
fn deploy_contract(
5787
self: @ContractState,

crates/starknet_os_flow_tests/src/virtual_os_test.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ async fn test_reverted_tx_os_error() {
276276
/// Reproduces a privacy-prove failure when proving a virtual OS run that uses the keccak builtin.
277277
///
278278
/// 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).
279+
/// - Invoke `test_contract::test_keccak`, which uses both `keccak::keccak_u256s_le_inputs` (keccak
280+
/// BUILTIN) and `syscalls::keccak_syscall` with invalid input length (keccak SYSCALL failure
281+
/// path).
282282
/// - Run through the virtual OS (succeeds), then prove via `privacy-prove` and verify.
283283
///
284284
/// Observed result: OS execution succeeds; proving panics in
@@ -288,10 +288,10 @@ async fn test_reverted_tx_os_error() {
288288
/// at `simple_bootloader/execute_task.cairo:209`.
289289
///
290290
/// 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.
291+
/// which has `keccak: None`
292+
/// (`cairo-vm-3.2.0/src/types/instance_definitions/builtins_instance_def.rs`). The bootloader
293+
/// simulates keccak via `simple_bootloader_simulate_keccak`, but the simulation does not appear to
294+
/// cover relocating an inner task PIE whose keccak builtin segment is non-empty.
295295
///
296296
/// Isolation experiment: commenting out the `keccak::keccak_u256s_le_inputs` call in
297297
/// `test_contract::test_keccak` (leaving only the keccak SYSCALL failure path) makes this test
@@ -318,12 +318,48 @@ async fn prove_and_verify_keccak_tx() {
318318

319319
let proof_facts = output.proof_facts.clone();
320320
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");
321+
tokio::task::spawn_blocking(move || starknet_proof_verifier::verify_proof(proof_facts, proof))
322+
.await
323+
.expect("proof verification task panicked")
324+
.expect("proof verification should succeed");
325+
}
326+
327+
/// Proves and verifies a virtual OS run of a single transaction that performs a
328+
/// multicall through the account contract. The inner calls can be extended over
329+
/// time to grow syscall/builtin coverage.
330+
#[tokio::test(flavor = "multi_thread")]
331+
#[ignore]
332+
async fn prove_and_verify_multicall_tx() {
333+
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
334+
335+
let (mut test_builder, [contract_address]) =
336+
TestBuilder::create_standard_virtual([(test_contract, calldata![Felt::ONE, Felt::TWO])])
337+
.await;
338+
339+
// Appends a single `Call` entry to `multi_call_args` as
340+
// `[to, selector, calldata_len, *calldata]` and bumps the leading `num_calls`
341+
// counter. All inner calls in this test target the same test contract.
342+
let mut multi_call_args: Vec<Felt> = vec![Felt::ZERO];
343+
let mut serialize_call = |func_name: &str, args: &[Felt]| {
344+
multi_call_args[0] += Felt::ONE;
345+
multi_call_args.push(*contract_address.0.key());
346+
multi_call_args.push(selector_from_name(func_name).0);
347+
multi_call_args.push(Felt::from(args.len()));
348+
multi_call_args.extend_from_slice(args);
349+
};
350+
351+
// TODO(Yoni): add more inner calls (e.g. sha256, secp256k1, send_message_to_l1).
352+
serialize_call("test_keccak", &[]);
353+
serialize_call("test_ec_op", &[]);
354+
355+
// The dummy account's `__execute__(contract_address, selector, calldata)` forwards
356+
// to its own `multi_call` entry point.
357+
let calldata = create_calldata(*FUNDED_ACCOUNT_ADDRESS, "multi_call", &multi_call_args);
358+
test_builder.add_funded_account_invoke(invoke_tx_args! { calldata });
359+
360+
let output = test_builder.build().await.run_virtual().prove().await;
361+
starknet_proof_verifier::verify_proof(output.proof_facts, output.proof)
362+
.expect("proof verification should succeed");
327363
}
328364

329365
/// Generates proof fixtures for the proof-flow integration test.

crates/starknet_transaction_prover/src/proving/virtual_snos_prover_test.rs

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -44,44 +44,6 @@ use crate::test_utils::{
4444
STRK_TOKEN_ADDRESS_SEPOLIA,
4545
};
4646

47-
/// Integration test for the full prover pipeline with a `balanceOf` transaction.
48-
/// Runs on a Sepolia environment; in live/recording mode requires a Sepolia RPC node via
49-
/// `NODE_URL`.
50-
#[rstest]
51-
#[tokio::test(flavor = "multi_thread")]
52-
#[ignore]
53-
async fn test_prove_balance_of_transaction() {
54-
let test_mode = resolve_test_mode("test_prove_balance_of_transaction").await;
55-
56-
// Creates an RPC invoke transaction that calls `balanceOf` on the STRK token.
57-
let strk_token = ContractAddress::try_from(STRK_TOKEN_ADDRESS_SEPOLIA).unwrap();
58-
let account = ContractAddress::try_from(DUMMY_ACCOUNT_ADDRESS).unwrap();
59-
60-
// Calldata matches dummy account's __execute__(contract_address, selector, calldata).
61-
let calldata = create_calldata(strk_token, "balanceOf", &[account.into()]);
62-
let rpc_tx = build_client_side_rpc_invoke(account, calldata);
63-
64-
let factory = runner_factory(&test_mode.rpc_url());
65-
let prover = VirtualSnosProver::from_runner(factory);
66-
67-
// Run the full prover pipeline: OS execution → proof generation.
68-
let result = prover.prove_transaction(BlockId::Latest, rpc_tx).await;
69-
70-
// Finalize recording before asserting so records are saved even on failure.
71-
test_mode.finalize();
72-
73-
// Verify execution and proving succeeded.
74-
let output = result.expect("prove_transaction should succeed");
75-
76-
// Verify the proof against the proof facts.
77-
let proof_facts = output.proof_facts.clone();
78-
let proof = output.proof.clone();
79-
tokio::task::spawn_blocking(move || verify_proof(proof_facts, proof))
80-
.await
81-
.expect("proof verification task panicked")
82-
.expect("proof verification should succeed");
83-
}
84-
8547
/// Integration test for the full prover pipeline with a STRK `transfer` transaction.
8648
/// Runs on a Sepolia environment; in live/recording mode requires a Sepolia RPC node via
8749
/// `NODE_URL`.

0 commit comments

Comments
 (0)