@@ -18,13 +18,18 @@ use apollo_consensus_orchestrator::cende::{
1818 InternalTransactionWithReceipt ,
1919} ;
2020use apollo_infra_utils:: compile_time_cargo_manifest_dir;
21+ use blockifier:: abi:: constants:: STORED_BLOCK_HASH_BUFFER ;
22+ use blockifier:: blockifier:: config:: TransactionExecutorConfig ;
23+ use blockifier:: blockifier:: transaction_executor:: TransactionExecutor ;
2124use blockifier:: blockifier_versioned_constants:: VersionedConstants ;
2225use blockifier:: bouncer:: BouncerConfig ;
2326use blockifier:: context:: { BlockContext , ChainInfo } ;
24- use blockifier:: state:: cached_state:: { CachedState , StateMaps } ;
27+ use blockifier:: state:: cached_state:: { CachedState , CommitmentStateDiff , StateMaps } ;
28+ use blockifier:: state:: state_api:: UpdatableState ;
2529use blockifier:: test_utils:: contracts:: FeatureContractTrait ;
2630use blockifier:: test_utils:: dict_state_reader:: DictStateReader ;
2731use blockifier:: transaction:: account_transaction:: AccountTransaction as BlockifierAccountTx ;
32+ use blockifier:: transaction:: transaction_execution:: Transaction as BlockifierTx ;
2833use blockifier:: transaction:: transactions:: ExecutableTransaction ;
2934use blockifier_test_utils:: contracts:: FeatureContract ;
3035use expect_test:: expect_file;
@@ -35,23 +40,30 @@ use google_cloud_storage::http::objects::get::GetObjectRequest;
3540use google_cloud_storage:: http:: objects:: upload:: { Media , UploadObjectRequest , UploadType } ;
3641use google_cloud_storage:: http:: Error as GcsError ;
3742use mockall:: predicate:: eq;
38- use starknet_api:: block:: { BlockHash , BlockInfo , BlockNumber , BlockTimestamp } ;
39- use starknet_api:: block_hash:: block_hash_calculator:: PartialBlockHashComponents ;
43+ use starknet_api:: block:: { BlockHash , BlockHashAndNumber , BlockInfo , BlockNumber , BlockTimestamp } ;
44+ use starknet_api:: block_hash:: block_hash_calculator:: {
45+ calculate_block_commitments,
46+ calculate_block_hash,
47+ PartialBlockHashComponents ,
48+ TransactionHashingData ,
49+ } ;
4050use starknet_api:: consensus_transaction:: InternalConsensusTransaction ;
4151use starknet_api:: contract_address;
4252use starknet_api:: contract_class:: compiled_class_hash:: HashVersion ;
4353use starknet_api:: core:: { ChainId , Nonce , OsChainInfo } ;
44- use starknet_api:: data_availability:: DataAvailabilityMode ;
54+ use starknet_api:: data_availability:: { DataAvailabilityMode , L1DataAvailabilityMode } ;
4555use starknet_api:: executable_transaction:: {
4656 AccountTransaction as ExecutableAccountTx ,
47- DeclareTransaction as ExecutableDeclareTransaction ,
57+ DeclareTransaction as ExecutableDeclareTx ,
58+ Transaction as ExecutableTx ,
4859} ;
4960use starknet_api:: hash:: StateRoots ;
5061use starknet_api:: rpc_transaction:: {
5162 InternalRpcDeclareTransactionV3 ,
5263 InternalRpcTransaction ,
5364 InternalRpcTransactionWithoutTxHash ,
5465} ;
66+ use starknet_api:: state:: ThinStateDiff ;
5567use starknet_api:: test_utils:: TEST_SEQUENCER_ADDRESS ;
5668use starknet_api:: transaction:: fields:: {
5769 AccountDeploymentData ,
@@ -66,9 +78,11 @@ use starknet_api::transaction::{
6678 TransactionOffsetInBlock ,
6779 TransactionVersion ,
6880} ;
81+ use starknet_committer:: block_committer:: input:: StateDiff ;
6982use starknet_committer:: db:: facts_db:: FactsDb ;
7083use starknet_committer:: db:: forest_trait:: StorageInitializer ;
7184use starknet_patricia_storage:: map_storage:: MapStorage ;
85+ use starknet_transaction_prover:: running:: committer_utils:: commit_state_diff;
7286
7387const GCS_ERROR_CODE_NOT_FOUND : u16 = 404 ;
7488
@@ -121,7 +135,6 @@ struct BlobFactory {
121135
122136 // Context.
123137 state : DictStateReader ,
124- #[ expect( dead_code) ]
125138 committer_storage : FactsDb < MapStorage > ,
126139}
127140
@@ -140,13 +153,106 @@ impl BlobFactory {
140153 }
141154
142155 /// Executes the unblocked transactions and applies the changes to the state.
156+ /// Any subsequent transaction added will end up in the next block.
143157 #[ expect( dead_code) ]
144- fn close_block ( & mut self ) {
145- unimplemented ! ( )
158+ async fn close_block ( & mut self ) {
159+ let block_context = self . next_block_context ( ) ;
160+ let block_info = block_context. block_info ( ) . clone ( ) ;
161+ let block_number = block_info. block_number ;
162+
163+ // Execute the transactions.
164+ // If the block number is after the block hash buffer, set the previous block hash and
165+ // number, so they appear in the state diff.
166+ let old_block_number_and_hash = if block_number. 0 < STORED_BLOCK_HASH_BUFFER {
167+ None
168+ } else {
169+ let old_block_number = block_number. 0 - STORED_BLOCK_HASH_BUFFER ;
170+ Some ( BlockHashAndNumber {
171+ number : BlockNumber ( old_block_number) ,
172+ // If we are past the block hash buffer, this should never panic.
173+ hash : self . blocks [ usize:: try_from ( old_block_number) . unwrap ( ) ] . block_hash ,
174+ } )
175+ } ;
176+ let state_clone = self . state . clone ( ) ;
177+ let mut executor = TransactionExecutor :: pre_process_and_create (
178+ state_clone,
179+ block_context. clone ( ) ,
180+ old_block_number_and_hash,
181+ TransactionExecutorConfig :: create_for_testing ( false ) ,
182+ )
183+ . unwrap ( ) ;
184+ let mut transactions_with_receipts = Vec :: new ( ) ;
185+ // Consume the transactions list (next block starts empty).
186+ for ( executable, internal) in std:: mem:: take ( & mut self . next_txs ) . into_iter ( ) {
187+ let ( execution_info, _state_changes) = executor
188+ . execute ( & BlockifierTx :: new_for_sequencing ( ExecutableTx :: Account ( executable) ) )
189+ . unwrap ( ) ;
190+ assert ! ( !execution_info. is_reverted( ) , "Got a reverted tx: {execution_info:?}" ) ;
191+
192+ transactions_with_receipts
193+ . push ( InternalTransactionWithReceipt { transaction : internal, execution_info } ) ;
194+ }
195+ let summary = executor. non_consuming_finalize ( ) . unwrap ( ) ;
196+
197+ // Apply changes to state and create the multitude of state-diff-like objects required...
198+ // The [CommitterStateDiff] type is the blockifier representation of the committer's state
199+ // diff, whereas [StateDiff] is the committer's representation of the state diff.
200+ let committer_state_diff: CommitmentStateDiff = summary. state_diff . clone ( ) ;
201+ let thin_state_diff = ThinStateDiff :: from ( committer_state_diff. clone ( ) ) ;
202+ let state_diff = StateDiff :: from ( thin_state_diff. clone ( ) ) ;
203+ let state_maps = StateMaps :: from ( committer_state_diff. clone ( ) ) ;
204+ let class_mapping = executor. block_state . unwrap ( ) . class_hash_to_class . borrow ( ) . clone ( ) ;
205+ self . state . apply_writes ( & state_maps, & class_mapping) ;
206+
207+ // Commit the block.
208+ let prev_state_roots = self . last_finalized_state_roots ( ) ;
209+ let state_roots = commit_state_diff (
210+ & mut self . committer_storage ,
211+ prev_state_roots. contracts_trie_root_hash ,
212+ prev_state_roots. classes_trie_root_hash ,
213+ state_diff,
214+ )
215+ . await
216+ . expect ( "Failed to commit state diff." ) ;
217+
218+ // Compute the block hash.
219+ let transaction_hashing_data: Vec < _ > = transactions_with_receipts
220+ . iter ( )
221+ . map ( |tx| TransactionHashingData {
222+ transaction_signature : tx. transaction . tx_signature_for_commitment ( ) . unwrap ( ) ,
223+ transaction_output : tx. execution_info . output_for_hashing ( ) ,
224+ transaction_hash : tx. transaction . tx_hash ( ) ,
225+ } )
226+ . collect ( ) ;
227+ let ( block_header_commitments, _) = calculate_block_commitments (
228+ & transaction_hashing_data,
229+ thin_state_diff,
230+ L1DataAvailabilityMode :: from_use_kzg_da ( block_info. use_kzg_da ) ,
231+ & block_info. starknet_version ,
232+ )
233+ . await ;
234+ let partial_block_hash_components =
235+ PartialBlockHashComponents :: new ( & block_info, block_header_commitments) ;
236+ let block_hash = calculate_block_hash (
237+ & partial_block_hash_components,
238+ state_roots. global_root ( ) ,
239+ self . last_finalized_block_hash ( ) ,
240+ )
241+ . unwrap ( ) ;
242+
243+ // Create and push block data.
244+ self . blocks . push ( BlockData {
245+ block_context,
246+ transactions_with_receipts,
247+ partial_block_hash_components,
248+ block_hash,
249+ state_maps,
250+ state_roots,
251+ } ) ;
146252 }
147253
148254 /// Creates blobs for all finalized blocks, and a preconfirmed block with the remaining txs that
149- /// were not included in a block.
255+ /// were not included in a block. See [Self::close_block] for details on how to close a block.
150256 async fn finalize ( self ) -> ( Vec < AerospikeBlob > , CendeWritePreconfirmedBlock ) {
151257 // TODO(Dori): Create the blob vector.
152258 let blobs = vec ! [ ] ;
@@ -157,12 +263,10 @@ impl BlobFactory {
157263 ( blobs, preconfirmed_block)
158264 }
159265
160- #[ expect( dead_code) ]
161266 fn last_finalized_block_hash ( & self ) -> BlockHash {
162267 self . blocks . last ( ) . map ( |block| block. block_hash ) . unwrap_or ( BlockHash :: GENESIS_PARENT_HASH )
163268 }
164269
165- #[ expect( dead_code) ]
166270 fn last_finalized_state_roots ( & self ) -> StateRoots {
167271 self . blocks . last ( ) . map ( |block| block. state_roots ) . unwrap_or_default ( )
168272 }
@@ -173,7 +277,7 @@ impl BlobFactory {
173277
174278 #[ expect( dead_code) ]
175279 fn boostrap_declare_tx ( & mut self , contract : FeatureContract ) {
176- let sender_address = ExecutableDeclareTransaction :: bootstrap_address ( ) ;
280+ let sender_address = ExecutableDeclareTx :: bootstrap_address ( ) ;
177281 let sierra = contract. get_sierra ( ) ;
178282 let class_hash = sierra. calculate_class_hash ( ) ;
179283 let compiled_class_hash = contract. get_compiled_class_hash ( & HashVersion :: V2 ) ;
@@ -203,7 +307,7 @@ impl BlobFactory {
203307 } ) ;
204308
205309 // Create executable tx.
206- let executable = ExecutableDeclareTransaction :: create (
310+ let executable = ExecutableDeclareTx :: create (
207311 DeclareTransaction :: V3 ( internal_declare_without_hash. into ( ) ) ,
208312 contract. get_class_info ( ) ,
209313 & CHAIN_ID ,
0 commit comments