diff --git a/crates/blockifier/src/concurrency/scheduler.rs b/crates/blockifier/src/concurrency/scheduler.rs index 43e9168125..601a1302b3 100644 --- a/crates/blockifier/src/concurrency/scheduler.rs +++ b/crates/blockifier/src/concurrency/scheduler.rs @@ -149,6 +149,10 @@ impl Scheduler { } } + pub fn get_n_committed_txs(&self) -> usize { + *self.commit_index.lock().unwrap() + } + fn lock_tx_status(&self, tx_index: TxIndex) -> MutexGuard<'_, TransactionStatus> { lock_mutex_in_array(&self.tx_statuses, tx_index) } diff --git a/crates/blockifier/src/concurrency/worker_logic_test.rs b/crates/blockifier/src/concurrency/worker_logic_test.rs index c72a31e88d..2921a85dbf 100644 --- a/crates/blockifier/src/concurrency/worker_logic_test.rs +++ b/crates/blockifier/src/concurrency/worker_logic_test.rs @@ -92,7 +92,6 @@ fn test_worker_execute() { }); - // Concurrency settings. let txs = [tx_success, tx_failure, tx_revert] .into_iter() .map(Transaction::AccountTransaction) @@ -258,7 +257,6 @@ fn test_worker_validate() { }); - // Concurrency settings. let txs = [account_tx0, account_tx1] .into_iter() .map(Transaction::AccountTransaction) @@ -463,3 +461,107 @@ fn test_deploy_before_declare() { let next_task = worker_executor.validate(1); assert_eq!(next_task, Task::NoTask); } + +#[test] +fn test_worker_commit_phase() { + // Settings. + let concurrency_mode = true; + let block_context = + BlockContext::create_for_account_testing_with_concurrency_mode(concurrency_mode); + + let account_contract = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); + let test_contract = FeatureContract::TestContract(CairoVersion::Cairo0); + let chain_info = &block_context.chain_info; + + // Create the state. + let state_reader = + test_state_reader(chain_info, BALANCE, &[(account_contract, 1), (test_contract, 1)]); + let safe_versioned_state = safe_versioned_state_for_testing(state_reader); + + // Create transactions. + let test_contract_address = test_contract.get_instance_address(0); + let sender_address = account_contract.get_instance_address(0); + let nonce_manager = &mut NonceManager::default(); + let storage_value = stark_felt!(93_u8); + let storage_key = storage_key!(1993_u16); + let calldata = create_calldata( + test_contract_address, + "test_storage_read_write", + &[*storage_key.0.key(), storage_value], // Calldata: address, value. + ); + let max_fee = Fee(MAX_FEE); + + let account_tx0 = account_invoke_tx(invoke_tx_args! { + sender_address, + calldata: calldata.clone(), + max_fee, + nonce: nonce_manager.next(sender_address) + }); + + let account_tx1 = account_invoke_tx(invoke_tx_args! { + sender_address, + calldata: calldata.clone(), + max_fee, + nonce: nonce_manager.next(sender_address) + }); + + let account_tx2 = account_invoke_tx(invoke_tx_args! { + sender_address, + calldata, + max_fee, + nonce: nonce_manager.next(sender_address) + }); + + let txs = [account_tx0, account_tx1, account_tx2] + .into_iter() + .map(Transaction::AccountTransaction) + .collect::>(); + + let worker_executor = WorkerExecutor::new(safe_versioned_state.clone(), &txs, block_context); + + // Try to commit before any transaction is ready. + worker_executor.commit_while_possible().unwrap(); + + // Verify no transaction was committed. + assert_eq!(worker_executor.scheduler.get_n_committed_txs(), 0); + + // Creates 2 active tasks. + // Creating these tasks changes the status of the first two transactions to `Executing`. If we + // skip this step, executing them will panic when reaching `finish_execution` (as their status + // will be `ReadyToExecute` and not `Executing` as expected). + assert_eq!(worker_executor.scheduler.next_task(), Task::ExecutionTask(0)); + assert_eq!(worker_executor.scheduler.next_task(), Task::ExecutionTask(1)); + + // Execute the first two transactions. + worker_executor.execute(0); + worker_executor.execute(1); + + // Commit the first two transactions (only). + worker_executor.commit_while_possible().unwrap(); + + // Verify the commit index is now 2. + assert_eq!(worker_executor.scheduler.get_n_committed_txs(), 2); + + // Verify the status of the first two transactions is `Committed`. + assert_eq!(*worker_executor.scheduler.get_tx_status(0), TransactionStatus::Committed); + assert_eq!(*worker_executor.scheduler.get_tx_status(1), TransactionStatus::Committed); + + // Create the final execution task and execute it. + assert_eq!(worker_executor.scheduler.next_task(), Task::ExecutionTask(2)); + worker_executor.execute(2); + + // Commit the third (and last) transaction. + worker_executor.commit_while_possible().unwrap(); + + // Verify the commit index is now 3, the status of the last transaction is `Committed`, and the + // next task is `Done`. + assert_eq!(worker_executor.scheduler.get_n_committed_txs(), 3); + assert_eq!(*worker_executor.scheduler.get_tx_status(2), TransactionStatus::Committed); + assert_eq!(worker_executor.scheduler.next_task(), Task::Done); + + // Try to commit when all transactions are already committed. + worker_executor.commit_while_possible().unwrap(); + assert_eq!(worker_executor.scheduler.get_n_committed_txs(), 3); + + // TODO(Avi, 15/06/2024): Check the halt mechanism once the bouncer is supported. +}