Skip to content

Commit 6a5b663

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 832d97a commit 6a5b663

10 files changed

Lines changed: 88 additions & 47 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

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.

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-
108608775
807+
110756293
808808
"#]]
809809
.assert_debug_eq(&migration_sierra_gas.0);
810810
expect![[r#"
811-
266780662
811+
272117746
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/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/fuzz_tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,15 @@ static IS_CAIRO1: LazyLock<BTreeMap<ClassHash, bool>> = LazyLock::new(|| {
8888

8989
/// Initial fuzz contract addresses.
9090
static FUZZ_ADDRESS_ORCHESTRATOR_EXPECT: Expect =
91-
expect!["0x4c885880af2af2afc2b57ed77bd4dfacd8da1768c0de60a14d854d6aa681678"];
91+
expect!["0x727742e0b8d4b0ba3e4d1bac6cea5e523cfde238c694ddf78006f113ae1f7d6"];
9292
static FUZZ_ADDRESS_CAIRO1_A_EXPECT: Expect =
93-
expect!["0x3b22209e355bb17880121aad4621a7658a2daea2c0c7dd5159595c93ddfac67"];
93+
expect!["0x1ee6d084c36adff47c27ffaac99ae535ca9608067508fa722ceeedefbc82460"];
9494
static FUZZ_ADDRESS_CAIRO1_B_EXPECT: Expect =
95-
expect!["0x2c1ef35cf09a851d642b1b21141e19a49aacce41a65eeeb46decd24fc55da54"];
95+
expect!["0x48a60831f25e99646cdf88a1de37140e2809bfb99f5791e504707ab899d86cb"];
9696
static FUZZ_ADDRESS_CAIRO0_A_EXPECT: Expect =
97-
expect!["0x209b4338889acf7e7c4a0654b51e51bc874df786a12d77e95a2362711cfac6c"];
97+
expect!["0x515caf6295458c854d5a577786d6cdd615bed0c02432e09822546c9f408fda1"];
9898
static FUZZ_ADDRESS_CAIRO0_B_EXPECT: Expect =
99-
expect!["0x437a3c2706d0fe102ddccafa7169b616c8f4b4124f0761edc08e9283592caf0"];
99+
expect!["0x682ede4f50d9b42fe07911e67993682449ed63a69ba1f8c69764ff1d22e5e5d"];
100100
static FUZZ_ADDRESS_ORCHESTRATOR: LazyLock<ContractAddress> = LazyLock::new(|| {
101101
ContractAddress::try_from(felt!(FUZZ_ADDRESS_ORCHESTRATOR_EXPECT.data())).unwrap()
102102
});

crates/starknet_os_flow_tests/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1265,7 +1265,7 @@ async fn test_experimental_libfuncs_contract(#[values(true, false)] use_kzg_da:
12651265
.copied()
12661266
.unwrap_or(0);
12671267
expect![[r#"
1268-
564
1268+
569
12691269
"#]]
12701270
.assert_debug_eq(&blakes);
12711271

crates/starknet_os_flow_tests/src/virtual_os_test.rs

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

277+
/// Proves and verifies a virtual OS run of a single transaction that performs a
278+
/// multicall through the account contract. The inner calls can be extended over
279+
/// time to grow syscall/builtin coverage.
280+
#[tokio::test(flavor = "multi_thread")]
281+
#[ignore]
282+
async fn prove_and_verify_multicall_tx() {
283+
let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1(RunnableCairo1::Casm));
284+
285+
let (mut test_builder, [contract_address]) =
286+
TestBuilder::create_standard_virtual([(test_contract, calldata![Felt::ONE, Felt::TWO])])
287+
.await;
288+
289+
// Appends a single `Call` entry to `multi_call_args` as
290+
// `[to, selector, calldata_len, *calldata]` and bumps the leading `num_calls`
291+
// counter. All inner calls in this test target the same test contract.
292+
let mut multi_call_args: Vec<Felt> = vec![Felt::ZERO];
293+
let mut serialize_call = |func_name: &str, args: &[Felt]| {
294+
multi_call_args[0] += Felt::ONE;
295+
multi_call_args.push(*contract_address.0.key());
296+
multi_call_args.push(selector_from_name(func_name).0);
297+
multi_call_args.push(Felt::from(args.len()));
298+
multi_call_args.extend_from_slice(args);
299+
};
300+
301+
// TODO(Yoni): add more inner calls (e.g. sha256, secp256k1, send_message_to_l1).
302+
// TODO(Yoni): restore the keccak inner call once the keccak syscall is allowed in
303+
// virtual OS mode (added in a follow-up PR stacked on top of this one).
304+
// serialize_call("test_keccak", &[]);
305+
serialize_call("test_ec_op", &[]);
306+
307+
// The dummy account's `__execute__(contract_address, selector, calldata)` forwards
308+
// to its own `multi_call` entry point.
309+
let calldata = create_calldata(*FUNDED_ACCOUNT_ADDRESS, "multi_call", &multi_call_args);
310+
test_builder.add_funded_account_invoke(invoke_tx_args! { calldata });
311+
312+
let output = test_builder.build().await.run_virtual().prove().await;
313+
starknet_proof_verifier::verify_proof(output.proof_facts, output.proof)
314+
.expect("proof verification should succeed");
315+
}
316+
277317
/// Generates proof fixtures for the proof-flow integration test.
278318
/// To run manually: `cargo +nightly-2025-07-14 test -p starknet_os_flow_tests --features
279319
/// 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)