diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/aggregator/combine_blocks.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/aggregator/combine_blocks.cairo index 2316ec66f4f..10d727622a6 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/aggregator/combine_blocks.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/aggregator/combine_blocks.cairo @@ -89,6 +89,9 @@ func combine_blocks{range_check_ptr}( starknet_os_config_hash=first.header.starknet_os_config_hash, use_kzg_da=use_kzg_da, full_output=full_output, + processed_proof_output_low=0, + processed_proof_output_high=0, + n_proof_facts_transactions=0, ), squashed_os_state_update=first.squashed_os_state_update, initial_carried_outputs=initial_carried_outputs, @@ -98,6 +101,13 @@ func combine_blocks{range_check_ptr}( let res = combine_blocks_inner(aggregated=aggregated, n=n - 1, os_outputs=&os_outputs[1]); local res_state_update: SquashedOsStateUpdate = [res.squashed_os_state_update]; + let ( + local n_proof_facts_transactions, + local processed_proof_output_low, + local processed_proof_output_high, + ) = combine_proof_facts_folds(n=n, os_outputs=os_outputs); + local res_header: OsOutputHeader* = res.header; + %{ SetStateUpdatePointersToNone %} // Squash the contract state diff dict. @@ -117,7 +127,20 @@ func combine_blocks{range_check_ptr}( ); tempvar squashed_res = new OsOutput( - header=res.header, + header=new OsOutputHeader( + state_update_output=res_header.state_update_output, + prev_block_number=res_header.prev_block_number, + new_block_number=res_header.new_block_number, + prev_block_hash=res_header.prev_block_hash, + new_block_hash=res_header.new_block_hash, + os_program_hash=res_header.os_program_hash, + starknet_os_config_hash=res_header.starknet_os_config_hash, + use_kzg_da=res_header.use_kzg_da, + full_output=res_header.full_output, + processed_proof_output_low=processed_proof_output_low, + processed_proof_output_high=processed_proof_output_high, + n_proof_facts_transactions=n_proof_facts_transactions, + ), squashed_os_state_update=new SquashedOsStateUpdate( contract_state_changes=squashed_contract_state_dict, n_contract_state_changes=n_contract_state_changes, @@ -146,7 +169,7 @@ func combine_blocks_inner(aggregated: OsOutput*, n: felt, os_outputs: OsOutput*) // Check the size of `OsOutput` and `OsOutputHeader` to ensure that if new fields are added // they are handled by the aggregator. static_assert OsOutput.SIZE == 4; - static_assert OsOutputHeader.SIZE == 9; + static_assert OsOutputHeader.SIZE == 12; // Validate fields of the inner OS output of a single task. assert current_header.use_kzg_da = 0; @@ -192,6 +215,9 @@ func combine_blocks_inner(aggregated: OsOutput*, n: felt, os_outputs: OsOutput*) starknet_os_config_hash=aggregated_header.starknet_os_config_hash, use_kzg_da=aggregated_header.use_kzg_da, full_output=aggregated_header.full_output, + processed_proof_output_low=0, + processed_proof_output_high=0, + n_proof_facts_transactions=0, ), squashed_os_state_update=new SquashedOsStateUpdate( contract_state_changes=aggregated_update.contract_state_changes, @@ -207,3 +233,35 @@ func combine_blocks_inner(aggregated: OsOutput*, n: felt, os_outputs: OsOutput*) return combine_blocks_inner(aggregated=new_aggregated, n=n - 1, os_outputs=&os_outputs[1]); } + +func combine_proof_facts_folds(n: felt, os_outputs: OsOutput*) -> ( + n_proof_facts_transactions: felt, root_output_low: felt, root_output_high: felt +) { + alloc_locals; + let ( + local n_proof_facts_transactions, local root_output_low, local root_output_high + ) = sum_block_proof_facts_outputs(n=n, os_outputs=os_outputs); + assert n_proof_facts_transactions * (n_proof_facts_transactions - 1) = 0; + return ( + n_proof_facts_transactions=n_proof_facts_transactions, + root_output_low=root_output_low, + root_output_high=root_output_high, + ); +} + +func sum_block_proof_facts_outputs(n: felt, os_outputs: OsOutput*) -> ( + n_proof_facts_transactions: felt, root_output_low: felt, root_output_high: felt +) { + if (n == 0) { + return (n_proof_facts_transactions=0, root_output_low=0, root_output_high=0); + } + let ( + rest_n_transactions, rest_root_output_low, rest_root_output_high + ) = sum_block_proof_facts_outputs(n=n - 1, os_outputs=&os_outputs[1]); + let header = os_outputs[0].header; + return ( + n_proof_facts_transactions=rest_n_transactions + header.n_proof_facts_transactions, + root_output_low=rest_root_output_low + header.processed_proof_output_low, + root_output_high=rest_root_output_high + header.processed_proof_output_high, + ); +} diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/constants.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/constants.cairo index 96835943c9d..b1280eca975 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/constants.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/constants.cairo @@ -66,7 +66,7 @@ const STORED_BLOCK_HASH_BUFFER = 10; // Allowed virtual OS program hashes for client-side proving. const ALLOWED_VIRTUAL_OS_PROGRAM_HASHES_0 = ( - 0x053f6c9fcfd31d27279ff7d7e422b44623550a732b59fe193354a7316a96daa1 + 0x07dc1bd0f3938f5f2f5e7c14118402cd79f2867419dccd484532651dda9a36a4 ); const ALLOWED_VIRTUAL_OS_PROGRAM_HASHES_1 = ( 0x01c7be3225dfb33359b3ba9ffbe1542b7da27879b6d89f47b967e512463fd324 diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions.cairo index 804b9d7f053..b29c728f880 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions.cairo @@ -24,6 +24,7 @@ from starkware.starknet.core.os.execution.execute_transactions_inner import ( execute_transactions_inner, ) from starkware.starknet.core.os.output import OsCarriedOutputs +from starkware.starknet.core.os.proof_fact_fold import ProofFactsReference // Executes the transactions in the hint variable block_input.transactions. // @@ -49,6 +50,7 @@ func execute_transactions{ contract_state_changes: DictAccess*, contract_class_changes: DictAccess*, outputs: OsCarriedOutputs*, + proof_facts_references: ProofFactsReference*, txs_range_check_ptr, }(block_context: BlockContext*) { alloc_locals; diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner.cairo index 24c53b27682..9fa8adc4aac 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner.cairo @@ -8,6 +8,7 @@ from starkware.starknet.core.os.execution.transaction_impls import ( execute_l1_handler_transaction, ) from starkware.starknet.core.os.output import OsCarriedOutputs +from starkware.starknet.core.os.proof_fact_fold import ProofFactsReference // Inner function for execute_transactions. // Arguments: @@ -26,6 +27,7 @@ func execute_transactions_inner{ contract_state_changes: DictAccess*, contract_class_changes: DictAccess*, outputs: OsCarriedOutputs*, + proof_facts_references: ProofFactsReference*, }(block_context: BlockContext*, n_txs) { %{ LogRemainingTxs %} if (n_txs == 0) { diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner__virtual.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner__virtual.cairo index eefa6e62ccc..c33919429fa 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner__virtual.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/execute_transactions_inner__virtual.cairo @@ -7,6 +7,7 @@ from starkware.starknet.core.os.execution.transaction_impls import ( execute_invoke_function_transaction, ) from starkware.starknet.core.os.output import OsCarriedOutputs +from starkware.starknet.core.os.proof_fact_fold import ProofFactsReference // In virtual OS mode, we only support a single INVOKE_FUNCTION transaction. func execute_transactions_inner{ @@ -15,6 +16,7 @@ func execute_transactions_inner{ contract_state_changes: DictAccess*, contract_class_changes: DictAccess*, outputs: OsCarriedOutputs*, + proof_facts_references: ProofFactsReference*, }(block_context: BlockContext*, n_txs) { // Part of the VIRTUAL_SNOS0 version contract. Changes must trigger a version bump. with_attr error_message("Expected exactly one transaction") { diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/transaction_impls.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/transaction_impls.cairo index b89cdd2abde..adb9bb3a4d2 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/transaction_impls.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/execution/transaction_impls.cairo @@ -60,6 +60,10 @@ from starkware.starknet.core.os.output import ( OsCarriedOutputs, os_carried_outputs_new, ) +from starkware.starknet.core.os.proof_fact_fold import ( + ProofFactsReference, + record_proof_facts_reference, +) from starkware.starknet.core.os.state.commitment import StateEntry from starkware.starknet.core.os.transaction_hash.transaction_hash import ( CommonTxFields, @@ -254,6 +258,7 @@ func execute_invoke_function_transaction{ contract_state_changes: DictAccess*, contract_class_changes: DictAccess*, outputs: OsCarriedOutputs*, + proof_facts_references: ProofFactsReference*, }(block_context: BlockContext*) { alloc_locals; @@ -362,6 +367,8 @@ func execute_invoke_function_transaction{ %{ EndTx %} + record_proof_facts_reference(proof_facts_size=proof_facts_size, proof_facts=proof_facts); + return (); } diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os.cairo index 632f8fac31d..33e08cebc7a 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os.cairo @@ -49,6 +49,10 @@ from starkware.starknet.core.os.output import ( OsCarriedOutputs, OsOutput, ) +from starkware.starknet.core.os.proof_fact_fold import ( + ProofFactsReference, + single_processed_proof_output, +) from starkware.starknet.core.os.state.state import OsStateUpdate, state_update // The main entry point of the Starknet OS. @@ -219,12 +223,24 @@ func execute_blocks{ } // Execute transactions. + let (proof_facts_references_start: ProofFactsReference*) = alloc(); let outputs = initial_carried_outputs; - with contract_state_changes, contract_class_changes, outputs { + let proof_facts_references = proof_facts_references_start; + with contract_state_changes, contract_class_changes, outputs, proof_facts_references { execute_transactions(block_context=block_context); } let final_carried_outputs = outputs; + let ( + n_proof_facts_transactions, processed_proof_output_low, processed_proof_output_high + ) = single_processed_proof_output( + proof_facts_references_start=proof_facts_references_start, + proof_facts_references_end=proof_facts_references, + ); + local n_proof_facts_transactions = n_proof_facts_transactions; + local processed_proof_output_low = processed_proof_output_low; + local processed_proof_output_high = processed_proof_output_high; + // Update the state. %{ EnterScopeWithAliases %} let (squashed_os_state_update, state_update_output) = state_update{hash_ptr=pedersen_ptr}( @@ -243,6 +259,9 @@ func execute_blocks{ block_context=block_context, state_update_output=state_update_output, os_global_context=os_global_context, + n_proof_facts_transactions=n_proof_facts_transactions, + processed_proof_output_low=processed_proof_output_low, + processed_proof_output_high=processed_proof_output_high, ); assert os_output_per_block_dst[0] = OsOutput( header=os_output_header, diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils.cairo index b1a5d68c91e..b6839fca491 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils.cairo @@ -122,6 +122,9 @@ func get_block_os_output_header{poseidon_ptr: PoseidonBuiltin*}( block_context: BlockContext*, state_update_output: CommitmentUpdate*, os_global_context: OsGlobalContext*, + n_proof_facts_transactions: felt, + processed_proof_output_low: felt, + processed_proof_output_high: felt, ) -> OsOutputHeader* { // Calculate the block hash based on the block info and state root. // NOTE: both the previous block hash and previous state root are guessed, and the OS @@ -145,6 +148,9 @@ func get_block_os_output_header{poseidon_ptr: PoseidonBuiltin*}( starknet_os_config_hash=os_global_context.starknet_os_config_hash, use_kzg_da=FALSE, full_output=TRUE, + processed_proof_output_low=processed_proof_output_low, + processed_proof_output_high=processed_proof_output_high, + n_proof_facts_transactions=n_proof_facts_transactions, ); return os_output_header; } diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils__virtual.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils__virtual.cairo index b9deacca9ed..1f35dd7da67 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils__virtual.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/os_utils__virtual.cairo @@ -52,7 +52,11 @@ func get_block_os_output_header{poseidon_ptr: PoseidonBuiltin*}( block_context: BlockContext*, state_update_output: CommitmentUpdate*, os_global_context: OsGlobalContext*, + n_proof_facts_transactions: felt, + processed_proof_output_low: felt, + processed_proof_output_high: felt, ) -> OsOutputHeader* { + assert n_proof_facts_transactions = 0; // Calculate the previous block hash based on the block info and the **initial** state root. let (_prev_prev_block_hash, prev_block_hash) = get_block_hashes{poseidon_ptr=poseidon_ptr}( block_info=block_context.block_info_for_execute, state_root=state_update_output.initial_root @@ -68,6 +72,9 @@ func get_block_os_output_header{poseidon_ptr: PoseidonBuiltin*}( starknet_os_config_hash=os_global_context.starknet_os_config_hash, use_kzg_da=FALSE, full_output=TRUE, + processed_proof_output_low=0, + processed_proof_output_high=0, + n_proof_facts_transactions=0, ); return os_output_header; } diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/output.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/output.cairo index a6831bf4770..f8e043ec3d2 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/output.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/output.cairo @@ -46,6 +46,9 @@ struct OsOutputHeader { use_kzg_da: felt, // Indicates whether previous state values are included in the state update information. full_output: felt, + processed_proof_output_low: felt, + processed_proof_output_high: felt, + n_proof_facts_transactions: felt, } // An L2 to L1 message header, the message payload is concatenated to the end of the header. @@ -168,6 +171,9 @@ func serialize_output_header{output_ptr: felt*}(os_output_header: OsOutputHeader serialize_word(os_output_header.starknet_os_config_hash); serialize_word(os_output_header.use_kzg_da); serialize_word(os_output_header.full_output); + serialize_word(os_output_header.processed_proof_output_low); + serialize_word(os_output_header.processed_proof_output_high); + serialize_word(os_output_header.n_proof_facts_transactions); return (); } diff --git a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/proof_fact_fold.cairo b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/proof_fact_fold.cairo index b6e4a30e96b..4d67b4e06b4 100644 --- a/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/proof_fact_fold.cairo +++ b/crates/apollo_starknet_os_program/src/cairo/starkware/starknet/core/os/proof_fact_fold.cairo @@ -6,6 +6,57 @@ from starkware.cairo.common.registers import get_label_location const BLAKE2S_DIGEST_N_WORDS = 8; const PROOF_ENTRY_N_WORDS = 2 * BLAKE2S_DIGEST_N_WORDS; +struct ProofFactsReference { + proof_facts_size: felt, + proof_facts: felt*, +} + +func record_proof_facts_reference{proof_facts_references: ProofFactsReference*}( + proof_facts_size: felt, proof_facts: felt* +) { + if (proof_facts_size == 0) { + return (); + } + assert [proof_facts_references] = ProofFactsReference( + proof_facts_size=proof_facts_size, proof_facts=proof_facts + ); + let proof_facts_references = &proof_facts_references[1]; + return (); +} + +func single_processed_proof_output{range_check_ptr}( + proof_facts_references_start: ProofFactsReference*, + proof_facts_references_end: ProofFactsReference*, +) -> ( + n_proof_facts_transactions: felt, + processed_proof_output_low: felt, + processed_proof_output_high: felt, +) { + alloc_locals; + local n_proof_facts_transactions = (proof_facts_references_end - proof_facts_references_start) / + ProofFactsReference.SIZE; + if (n_proof_facts_transactions == 0) { + return ( + n_proof_facts_transactions=0, + processed_proof_output_low=0, + processed_proof_output_high=0, + ); + } + assert n_proof_facts_transactions = 1; + let (output_digest) = compute_processed_proof_output_digest( + proof_facts_size=proof_facts_references_start.proof_facts_size, + proof_facts=proof_facts_references_start.proof_facts, + ); + let (processed_proof_output_low, processed_proof_output_high) = pack_output_digest( + output_digest=output_digest + ); + return ( + n_proof_facts_transactions=1, + processed_proof_output_low=processed_proof_output_low, + processed_proof_output_high=processed_proof_output_high, + ); +} + func compute_processed_proof_output_digest{range_check_ptr}( proof_facts_size: felt, proof_facts: felt* ) -> (output_digest: felt*) { diff --git a/crates/apollo_starknet_os_program/src/program_hash.json b/crates/apollo_starknet_os_program/src/program_hash.json index 7423fed0631..c4f91b4e343 100644 --- a/crates/apollo_starknet_os_program/src/program_hash.json +++ b/crates/apollo_starknet_os_program/src/program_hash.json @@ -1,6 +1,6 @@ { - "os": "0x1420d23187254d4dcf4006fa6ad997b0728f845416bcfd1478c7e8f804fb429", - "virtual_os": "0x1c7be3225dfb33359b3ba9ffbe1542b7da27879b6d89f47b967e512463fd324", - "aggregator": "0x3e4ce8340259e374200ed856e597a0c0b268d1021119d33573d7c759c5320f9", - "aggregator_with_prefix": "0x2526c12112a2d7f57f7ae74af7be1fe1b2766e4c34b0c88ceda16b70ed2d6c2" + "os": "0x3932c2db6cc720a69e8932bf62e07d90263ae1c2308ef68a096a7cb081bdd13", + "virtual_os": "0x7dc1bd0f3938f5f2f5e7c14118402cd79f2867419dccd484532651dda9a36a4", + "aggregator": "0x38b41528311efc46b895d4326121ebe25042cb694deabc4ec461b1653b89fc0", + "aggregator_with_prefix": "0x1015fe136b9f9ba7728018f2b4d40edb1ebe1dbccbcb8524b543640f8f1e724" } \ No newline at end of file diff --git a/crates/apollo_starknet_os_program/src/virtual_os_test.rs b/crates/apollo_starknet_os_program/src/virtual_os_test.rs index 32cd714d78a..df1eba618b2 100644 --- a/crates/apollo_starknet_os_program/src/virtual_os_test.rs +++ b/crates/apollo_starknet_os_program/src/virtual_os_test.rs @@ -19,11 +19,11 @@ fn test_virtual_os_swapped_files() { #[test] fn test_program_bytecode_lengths() { expect![[r#" - 16376 + 16663 "#]] .assert_debug_eq(&OS_PROGRAM.data_len()); expect![[r#" - 11426 + 11626 "#]] .assert_debug_eq(&VIRTUAL_OS_PROGRAM.data_len()); } diff --git a/crates/blockifier/resources/blockifier_versioned_constants_0_14_5.json b/crates/blockifier/resources/blockifier_versioned_constants_0_14_5.json index c9254ca531e..cd4efc4b73d 100644 --- a/crates/blockifier/resources/blockifier_versioned_constants_0_14_5.json +++ b/crates/blockifier/resources/blockifier_versioned_constants_0_14_5.json @@ -127,7 +127,7 @@ "segment_arena_cells": false, "os_constants": { "allowed_virtual_os_program_hashes": [ - "0x53f6c9fcfd31d27279ff7d7e422b44623550a732b59fe193354a7316a96daa1", + "0x7dc1bd0f3938f5f2f5e7c14118402cd79f2867419dccd484532651dda9a36a4", "0x1c7be3225dfb33359b3ba9ffbe1542b7da27879b6d89f47b967e512463fd324" ], "allowed_proof_versions": [ @@ -536,7 +536,7 @@ }, "InvokeFunction": { "constant": { - "n_steps": 4779, + "n_steps": 4794, "n_memory_holes": 0, "builtin_instance_counter": { "range_check_builtin": 110, diff --git a/crates/starknet_os/src/hints/hint_implementation/aggregator/test.rs b/crates/starknet_os/src/hints/hint_implementation/aggregator/test.rs index 095a46fac1e..504a55ff2ee 100644 --- a/crates/starknet_os/src/hints/hint_implementation/aggregator/test.rs +++ b/crates/starknet_os/src/hints/hint_implementation/aggregator/test.rs @@ -311,6 +311,9 @@ fn multi_block0_output(full_output: bool) -> Vec { Felt::ZERO, // full_output. Felt::from(full_output), + Felt::ZERO, + Felt::ZERO, + Felt::ZERO, // Messages to L1. MSG_TO_L1_0.len().into(), ], @@ -403,6 +406,9 @@ fn multi_block1_output(full_output: bool, modifier: FailureModifier) -> Vec Vec { Felt::from(use_kzg_da), // full_output. Felt::from(full_output), + Felt::ZERO, + Felt::ZERO, + Felt::ZERO, ], // KZG info. if use_kzg_da { combined_kzg_info(&da) } else { vec![] }, diff --git a/crates/starknet_os/src/hints/hint_implementation/aggregator/utils.rs b/crates/starknet_os/src/hints/hint_implementation/aggregator/utils.rs index af102a2866f..cc23c344dcb 100644 --- a/crates/starknet_os/src/hints/hint_implementation/aggregator/utils.rs +++ b/crates/starknet_os/src/hints/hint_implementation/aggregator/utils.rs @@ -271,6 +271,9 @@ pub(crate) fn write_full_os_output( common_os_output.starknet_os_config_hash.into(), Felt::ZERO.into(), // use_kzg_da field (False in the aggregator input). Felt::from(full_output_flag).into(), + common_os_output.processed_proof_output_low.into(), + common_os_output.processed_proof_output_high.into(), + Felt::from(common_os_output.n_proof_facts_transactions).into(), ])?; let squashed_os_state_update = vm.gen_arg(&vec![ diff --git a/crates/starknet_os/src/io/os_output.rs b/crates/starknet_os/src/io/os_output.rs index 717072ba3e4..a1f1ced63cc 100644 --- a/crates/starknet_os/src/io/os_output.rs +++ b/crates/starknet_os/src/io/os_output.rs @@ -257,6 +257,12 @@ impl TryFromOutputIter for OutputIterParsedData { let starknet_os_config_hash = wrap_missing(output_iter.next(), "starknet_os_config_hash")?; let use_kzg_da = wrap_missing_as_bool(output_iter.next(), "use_kzg_da")?; let full_output = wrap_missing_as_bool(output_iter.next(), "full_output")?; + let processed_proof_output_low = + wrap_missing(output_iter.next(), "processed_proof_output_low")?; + let processed_proof_output_high = + wrap_missing(output_iter.next(), "processed_proof_output_high")?; + let n_proof_facts_transactions = + wrap_missing_as(output_iter.next(), "n_proof_facts_transactions")?; let kzg_commitment_info = if use_kzg_da { Some(OsKzgCommitmentInfo::try_from_output_iter(output_iter, private_keys)?) @@ -300,6 +306,9 @@ impl TryFromOutputIter for OutputIterParsedData { new_block_hash, os_program_hash, starknet_os_config_hash, + processed_proof_output_low, + processed_proof_output_high, + n_proof_facts_transactions, messages_to_l1, messages_to_l2, }, @@ -328,6 +337,9 @@ pub struct CommonOsOutput { pub os_program_hash: StarkHash, // The hash of the OS config. pub starknet_os_config_hash: StarkHash, + pub processed_proof_output_low: StarkHash, + pub processed_proof_output_high: StarkHash, + pub n_proof_facts_transactions: usize, // Messages from L2 to L1. pub messages_to_l1: Vec, // Messages from L1 to L2. diff --git a/crates/starknet_os/src/proof_fact_fold.rs b/crates/starknet_os/src/proof_fact_fold.rs index fe543ce7a97..491d413e00b 100644 --- a/crates/starknet_os/src/proof_fact_fold.rs +++ b/crates/starknet_os/src/proof_fact_fold.rs @@ -58,3 +58,12 @@ pub fn compute_verification_digest( &[MULTIVERIFIER_CIRCUIT_HASH.as_slice(), processed_proof_output_digest.as_slice()].concat(), ) } + +pub fn pack_output_digest(output_digest: &Blake2sDigestWords) -> (Felt, Felt) { + let pack_half = |words: &[u32]| { + words.iter().rev().fold(Felt::ZERO, |packed_half, word| { + packed_half * Felt::from(1u64 << 32) + Felt::from(*word) + }) + }; + (pack_half(&output_digest[..4]), pack_half(&output_digest[4..])) +} diff --git a/crates/starknet_os/src/proof_fact_fold_test.rs b/crates/starknet_os/src/proof_fact_fold_test.rs index 01b10f3d278..4850a11f616 100644 --- a/crates/starknet_os/src/proof_fact_fold_test.rs +++ b/crates/starknet_os/src/proof_fact_fold_test.rs @@ -11,6 +11,7 @@ use super::{ compute_leaf_output_digest, compute_processed_proof_output_digest, compute_verification_digest, + pack_output_digest, Blake2sDigestWords, BLAKE2S_DIGEST_N_WORDS, LEAF_VERIFIER_CIRCUIT_HASH, @@ -22,6 +23,7 @@ use crate::test_utils::cairo_runner::{ EntryPointRunnerConfig, ImplicitArg, PointerArg, + ValueArg, }; fn entrypoint_runner_config() -> EntryPointRunnerConfig { @@ -213,3 +215,29 @@ fn test_cairo_circuit_hashes_match_rust() { MULTIVERIFIER_CIRCUIT_HASH ); } + +#[test] +fn test_cairo_pack_output_digest_matches_rust() { + let output_digest = compute_processed_proof_output_digest(&synthetic_proof_facts(0)); + let (expected_low, expected_high) = pack_output_digest(&output_digest); + let expected_return_values = vec![EndpointArg::from(Felt::ZERO), EndpointArg::from(Felt::ZERO)]; + let (_, packed_return_values, _) = initialize_and_run_cairo_0_entry_point( + &entrypoint_runner_config(), + PROOF_FACT_FOLD_BYTES, + "pack_output_digest", + &[felt_array_arg(&output_digest.map(Felt::from))], + &[], + &expected_return_values, + HashMap::new(), + None, + ) + .unwrap_or_else(|error| panic!("Failed to run pack_output_digest: {error:?}")); + let [ + EndpointArg::Value(ValueArg::Single(MaybeRelocatable::Int(cairo_low))), + EndpointArg::Value(ValueArg::Single(MaybeRelocatable::Int(cairo_high))), + ] = packed_return_values.as_slice() + else { + panic!("Expected pack_output_digest to return two felts."); + }; + assert_eq!((*cairo_low, *cairo_high), (expected_low, expected_high)); +} diff --git a/crates/starknet_os_flow_tests/src/test_manager.rs b/crates/starknet_os_flow_tests/src/test_manager.rs index 565054c065a..e13df975fa1 100644 --- a/crates/starknet_os_flow_tests/src/test_manager.rs +++ b/crates/starknet_os_flow_tests/src/test_manager.rs @@ -73,6 +73,7 @@ use starknet_os::io::os_output_types::{ TryFromOutputIter, }; use starknet_os::io::test_utils::validate_kzg_segment; +use starknet_os::proof_fact_fold::{compute_processed_proof_output_digest, pack_output_digest}; use starknet_os::runner::{run_os_stateless, DEFAULT_OS_LAYOUT}; use starknet_os::test_utils::coverage::expect_hint_coverage; use starknet_transaction_prover::running::committer_utils::{ @@ -158,6 +159,9 @@ pub(crate) struct OsTestExpectedValues { pub(crate) messages_to_l1: Vec, pub(crate) messages_to_l2: Vec, pub(crate) committed_state_diff: StateDiff, + pub(crate) processed_proof_output_low: Felt, + pub(crate) processed_proof_output_high: Felt, + pub(crate) n_proof_facts_transactions: usize, } impl OsTestExpectedValues { @@ -191,6 +195,9 @@ impl OsTestExpectedValues { first_block.block_info.starknet_version, ) .unwrap(); + let (processed_proof_output_low, processed_proof_output_high, n_proof_facts_transactions) = + expected_processed_proof_output(&os_hints.os_input.os_block_inputs); + Self { previous_global_root, new_global_root, @@ -205,7 +212,33 @@ impl OsTestExpectedValues { messages_to_l1, messages_to_l2, committed_state_diff, + processed_proof_output_low, + processed_proof_output_high, + n_proof_facts_transactions, + } + } +} + +fn expected_processed_proof_output(os_block_inputs: &[OsBlockInput]) -> (Felt, Felt, usize) { + let contributing_proof_facts: Vec<_> = os_block_inputs + .iter() + .flat_map(|block_input| &block_input.transactions) + .filter_map(|transaction| match transaction { + ExecutableTransaction::Account(AccountTransaction::Invoke(invoke_transaction)) => { + let proof_facts = invoke_transaction.proof_facts(); + (!proof_facts.0.is_empty()).then_some(proof_facts) + } + _ => None, + }) + .collect(); + match contributing_proof_facts.as_slice() { + [] => (Felt::ZERO, Felt::ZERO, 0), + [proof_facts] => { + let output_digest = compute_processed_proof_output_digest(&proof_facts.0); + let (output_low, output_high) = pack_output_digest(&output_digest); + (output_low, output_high, 1) } + _ => panic!("At most one transaction with proof facts is supported."), } } @@ -324,6 +357,19 @@ impl OsTestOutput { assert_eq!(os_output.use_kzg_da(), self.expected_values.use_kzg_da); assert_eq!(os_output.full_output(), self.expected_values.full_output); + assert_eq!( + os_output.common_os_output.processed_proof_output_low, + self.expected_values.processed_proof_output_low + ); + assert_eq!( + os_output.common_os_output.processed_proof_output_high, + self.expected_values.processed_proof_output_high + ); + assert_eq!( + os_output.common_os_output.n_proof_facts_transactions, + self.expected_values.n_proof_facts_transactions + ); + // KZG commitment. if os_output.use_kzg_da() { let OsStateDiff::PartialCommitment(PartialCommitmentOsStateDiff(