Skip to content

Commit 7b4a4d0

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 a967e07 commit 7b4a4d0

4 files changed

Lines changed: 75 additions & 38 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_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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,44 @@ async fn prove_and_verify_keccak_tx() {
326326
.expect("proof verification should succeed");
327327
}
328328

329+
/// Proves and verifies a virtual OS run of a single transaction that performs a
330+
/// multicall through the account contract. The inner calls can be extended over
331+
/// time to grow syscall/builtin coverage.
332+
#[tokio::test(flavor = "multi_thread")]
333+
#[ignore]
334+
async fn prove_and_verify_multicall_tx() {
335+
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
336+
337+
let (mut test_builder, [contract_address]) =
338+
TestBuilder::create_standard_virtual([(test_contract, calldata![Felt::ONE, Felt::TWO])])
339+
.await;
340+
341+
// Appends a single `Call` entry to `multi_call_args` as
342+
// `[to, selector, calldata_len, *calldata]` and bumps the leading `num_calls`
343+
// counter. All inner calls in this test target the same test contract.
344+
let mut multi_call_args: Vec<Felt> = vec![Felt::ZERO];
345+
let mut serialize_call = |func_name: &str, args: &[Felt]| {
346+
multi_call_args[0] = multi_call_args[0] + Felt::ONE;
347+
multi_call_args.push(*contract_address.0.key());
348+
multi_call_args.push(selector_from_name(func_name).0);
349+
multi_call_args.push(Felt::from(args.len()));
350+
multi_call_args.extend_from_slice(args);
351+
};
352+
353+
// TODO(Yoni): add more inner calls (e.g. sha256, secp256k1, send_message_to_l1).
354+
serialize_call("test_keccak", &[]);
355+
serialize_call("test_ec_op", &[]);
356+
357+
// The dummy account's `__execute__(contract_address, selector, calldata)` forwards
358+
// to its own `multi_call` entry point.
359+
let calldata = create_calldata(*FUNDED_ACCOUNT_ADDRESS, "multi_call", &multi_call_args);
360+
test_builder.add_funded_account_invoke(invoke_tx_args! { calldata });
361+
362+
let output = test_builder.build().await.run_virtual().prove().await;
363+
starknet_proof_verifier::verify_proof(output.proof_facts.clone(), output.proof.clone())
364+
.expect("proof verification should succeed");
365+
}
366+
329367
/// Generates proof fixtures for the proof-flow integration test.
330368
/// To run manually: `cargo +nightly-2025-07-14 test -p starknet_os_flow_tests --features
331369
/// starknet_transaction_prover/stwo_proving --release generate_proof_fixtures -- --ignored`

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)