Skip to main content

sui_core/execution_scheduler/
execution_scheduler_impl.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{
5    accumulators::funds_read::AccountFundsRead,
6    authority::{
7        AuthorityMetrics, ExecutionEnv, authority_per_epoch_store::AuthorityPerEpochStore,
8        shared_object_version_manager::Schedulable,
9    },
10    execution_cache::{ObjectCacheRead, TransactionCacheRead},
11    execution_scheduler::{
12        ExecutingGuard, PendingCertificateStats,
13        causal_admission::CausalAdmission,
14        funds_withdraw_scheduler::{
15            AddressFundsSchedulerMetrics, FundsSettlement, ScheduleStatus, TxFundsWithdraw,
16            WithdrawReservations, scheduler::FundsWithdrawScheduler,
17        },
18    },
19};
20use futures::stream::{FuturesUnordered, StreamExt};
21use mysten_common::ZipDebugEqIteratorExt;
22use mysten_common::{assert_reachable, debug_fatal};
23use mysten_metrics::spawn_monitored_task;
24use parking_lot::Mutex;
25use std::{
26    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
27    sync::Arc,
28};
29use sui_config::node::{AuthorityOverloadConfig, FundsWithdrawSchedulerType};
30use sui_types::{
31    SUI_ACCUMULATOR_ROOT_OBJECT_ID,
32    base_types::{FullObjectID, ObjectID, SequenceNumber},
33    digests::TransactionDigest,
34    error::SuiResult,
35    executable_transaction::VerifiedExecutableTransaction,
36    storage::InputKey,
37    transaction::{
38        SenderSignedData, SharedInputObject, SharedObjectMutability, TransactionData,
39        TransactionDataAPI, TransactionKey,
40    },
41};
42use tokio::sync::mpsc::UnboundedSender;
43use tokio::time::Instant;
44use tracing::{debug, instrument};
45
46use super::{PendingCertificate, overload_tracker::OverloadTracker};
47
48/// Utility struct for collecting barrier dependencies
49pub(crate) struct BarrierDependencyBuilder {
50    dep_state: BTreeMap<ObjectID, BTreeSet<TransactionDigest>>,
51}
52
53impl BarrierDependencyBuilder {
54    pub fn new() -> Self {
55        Self {
56            dep_state: Default::default(),
57        }
58    }
59
60    /// process_tx must be called for each transaction in scheduling order. If the
61    /// transaction has a non-exclusive write to an object, the transaction digest is
62    /// stored to become a dependency of the eventual barrier transaction. If a
63    /// transaction has an exclusive write to an object, all pending non-exclusive write
64    /// transactions for that object are added to the barrier dependencies.
65    pub fn process_tx(
66        &mut self,
67        tx_digest: TransactionDigest,
68        tx: &TransactionData,
69    ) -> BTreeSet<TransactionDigest> {
70        let mut barrier_deps = BTreeSet::new();
71        for SharedInputObject { id, mutability, .. } in tx.kind().shared_input_objects() {
72            match mutability {
73                SharedObjectMutability::NonExclusiveWrite => {
74                    self.dep_state.entry(id).or_default().insert(tx_digest);
75                }
76                SharedObjectMutability::Mutable => {
77                    // If there were preceding non-exclusive writes to this object id, this
78                    // transaction is a barrier and must wait for them to finish.
79                    if let Some(deps) = self.dep_state.remove(&id) {
80                        barrier_deps.extend(deps);
81                    }
82                }
83                SharedObjectMutability::Immutable => (),
84            }
85        }
86        barrier_deps
87    }
88}
89
90#[derive(Clone)]
91pub struct ExecutionScheduler {
92    object_cache_read: Arc<dyn ObjectCacheRead>,
93    transaction_cache_read: Arc<dyn TransactionCacheRead>,
94    overload_tracker: Arc<OverloadTracker>,
95    tx_ready_certificates: UnboundedSender<PendingCertificate>,
96    causal_admission: Arc<CausalAdmission>,
97    address_funds_withdraw_scheduler: Arc<Mutex<Option<FundsWithdrawScheduler>>>,
98    funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
99    metrics: Arc<AuthorityMetrics>,
100    address_funds_scheduler_metrics: Arc<AddressFundsSchedulerMetrics>,
101}
102
103struct PendingGuard<'a> {
104    scheduler: &'a ExecutionScheduler,
105    cert: &'a VerifiedExecutableTransaction,
106}
107
108impl<'a> PendingGuard<'a> {
109    pub fn new(scheduler: &'a ExecutionScheduler, cert: &'a VerifiedExecutableTransaction) -> Self {
110        scheduler
111            .metrics
112            .transaction_manager_num_pending_certificates
113            .inc();
114        scheduler
115            .overload_tracker
116            .add_pending_certificate(cert.data());
117        Self { scheduler, cert }
118    }
119}
120
121impl Drop for PendingGuard<'_> {
122    fn drop(&mut self) {
123        self.scheduler
124            .metrics
125            .transaction_manager_num_pending_certificates
126            .dec();
127        self.scheduler
128            .overload_tracker
129            .remove_pending_certificate(self.cert.data());
130    }
131}
132
133impl ExecutionScheduler {
134    pub fn new(
135        object_cache_read: Arc<dyn ObjectCacheRead>,
136        account_funds_read: Arc<dyn AccountFundsRead>,
137        transaction_cache_read: Arc<dyn TransactionCacheRead>,
138        tx_ready_certificates: UnboundedSender<PendingCertificate>,
139        epoch_store: &Arc<AuthorityPerEpochStore>,
140        funds_withdraw_scheduler_type: FundsWithdrawSchedulerType,
141        metrics: Arc<AuthorityMetrics>,
142        prometheus_registry: &prometheus::Registry,
143    ) -> Self {
144        tracing::info!(
145            ?funds_withdraw_scheduler_type,
146            "Creating new ExecutionScheduler"
147        );
148        let address_funds_scheduler_metrics =
149            Arc::new(AddressFundsSchedulerMetrics::new(prometheus_registry));
150        let address_funds_withdraw_scheduler = Self::initialize_funds_withdraw_scheduler(
151            epoch_store,
152            &object_cache_read,
153            account_funds_read,
154            funds_withdraw_scheduler_type,
155            &address_funds_scheduler_metrics,
156        );
157        Self {
158            object_cache_read,
159            transaction_cache_read,
160            overload_tracker: Arc::new(OverloadTracker::new()),
161            tx_ready_certificates,
162            causal_admission: CausalAdmission::new_with_default_sizing(prometheus_registry),
163            address_funds_withdraw_scheduler: Arc::new(Mutex::new(
164                address_funds_withdraw_scheduler,
165            )),
166            funds_withdraw_scheduler_type,
167            metrics,
168            address_funds_scheduler_metrics,
169        }
170    }
171
172    fn initialize_funds_withdraw_scheduler(
173        epoch_store: &Arc<AuthorityPerEpochStore>,
174        object_cache_read: &Arc<dyn ObjectCacheRead>,
175        account_funds_read: Arc<dyn AccountFundsRead>,
176        scheduler_type: FundsWithdrawSchedulerType,
177        address_funds_scheduler_metrics: &Arc<AddressFundsSchedulerMetrics>,
178    ) -> Option<FundsWithdrawScheduler> {
179        let withdraw_scheduler_enabled =
180            epoch_store.node_role().runs_consensus() && epoch_store.accumulators_enabled();
181        if !withdraw_scheduler_enabled {
182            return None;
183        }
184        let starting_accumulator_version = object_cache_read
185            .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
186            .expect("Accumulator root object must be present if funds accumulator is enabled")
187            .version();
188        let address_funds_withdraw_scheduler = FundsWithdrawScheduler::new(
189            account_funds_read.clone(),
190            starting_accumulator_version,
191            scheduler_type,
192            address_funds_scheduler_metrics.clone(),
193        );
194
195        Some(address_funds_withdraw_scheduler)
196    }
197
198    /// The causal-order state shared with the execution driver.
199    pub fn causal_admission(&self) -> &Arc<CausalAdmission> {
200        &self.causal_admission
201    }
202
203    #[instrument(level = "debug", skip_all, fields(tx_digest = ?cert.digest()))]
204    async fn schedule_transaction(
205        self,
206        cert: VerifiedExecutableTransaction,
207        execution_env: ExecutionEnv,
208        epoch_store: &Arc<AuthorityPerEpochStore>,
209    ) {
210        let enqueue_time = Instant::now();
211        let tx_digest = cert.digest();
212        let digests = [*tx_digest];
213
214        let tx_data = cert.transaction_data();
215        let input_object_kinds = tx_data
216            .input_objects()
217            .expect("input_objects() cannot fail");
218        let mut input_object_keys: Vec<_> = epoch_store
219            .get_input_object_keys(
220                &cert.key(),
221                &input_object_kinds,
222                &execution_env.assigned_versions,
223            )
224            .into_iter()
225            .collect();
226
227        // Coin reservation transactions need to wait for the accumulator root object
228        // to reach the assigned accumulator version. This ensures settlement transactions
229        // (which create/update accumulator account objects) execute before coin reservation
230        // transactions that depend on those objects.
231        if tx_data.kind().has_coin_reservations()
232            && let Some(accumulator_version) = execution_env.assigned_versions.accumulator_version()
233            && let Some(initial_shared_version) =
234                (**epoch_store.epoch_start_config()).accumulator_root_obj_initial_shared_version()
235        {
236            input_object_keys.push(InputKey::VersionedObject {
237                id: FullObjectID::new(SUI_ACCUMULATOR_ROOT_OBJECT_ID, Some(initial_shared_version)),
238                version: accumulator_version,
239            });
240        }
241
242        let receiving_object_keys: HashSet<_> = tx_data
243            .receiving_objects()
244            .into_iter()
245            .map(|entry| {
246                InputKey::VersionedObject {
247                    // TODO: Add support for receiving ConsensusV2 objects. For now this assumes fastpath.
248                    id: FullObjectID::new(entry.0, None),
249                    version: entry.1,
250                }
251            })
252            .collect();
253        let input_and_receiving_keys = [
254            input_object_keys,
255            receiving_object_keys.iter().cloned().collect(),
256        ]
257        .concat();
258
259        let epoch = epoch_store.epoch();
260        debug!(
261            ?tx_digest,
262            "Scheduled transaction, waiting for input objects: {:?}", input_and_receiving_keys,
263        );
264
265        let availability = self
266            .object_cache_read
267            .multi_input_objects_available_cache_only(&input_and_receiving_keys);
268        // Most of the times, the transaction's input objects are already available.
269        // We can check the availability of the input objects first, and only wait for the
270        // missing input objects if necessary.
271        let missing_input_keys: Vec<_> = input_and_receiving_keys
272            .into_iter()
273            .zip_debug_eq(availability)
274            .filter_map(|(key, available)| if !available { Some(key) } else { None })
275            .collect();
276
277        let has_missing_barrier_dependencies = self
278            .transaction_cache_read
279            .multi_get_executed_effects_digests(&execution_env.barrier_dependencies)
280            .into_iter()
281            .any(|r| r.is_none());
282
283        if missing_input_keys.is_empty() && !has_missing_barrier_dependencies {
284            self.metrics
285                .transaction_manager_num_enqueued_certificates
286                .with_label_values(&["ready"])
287                .inc();
288            debug!(?tx_digest, "Input objects already available");
289            self.send_transaction_for_execution(&cert, execution_env, enqueue_time);
290            return;
291        }
292
293        let _pending_guard = PendingGuard::new(&self, &cert);
294        self.metrics
295            .transaction_manager_num_enqueued_certificates
296            .with_label_values(&["pending"])
297            .inc();
298
299        if !execution_env.barrier_dependencies.is_empty() {
300            debug!(
301                "waiting for barrier dependencies to be executed: {:?}",
302                execution_env.barrier_dependencies
303            );
304            self.transaction_cache_read
305                .notify_read_executed_effects_digests(
306                    "wait_for_barrier_dependencies",
307                    &execution_env.barrier_dependencies,
308                )
309                .await;
310        }
311
312        tokio::select! {
313            _ = self.object_cache_read
314                .notify_read_input_objects(&missing_input_keys, &receiving_object_keys, epoch)
315                => {
316                    self.metrics
317                        .transaction_manager_transaction_queue_age_s
318                        .observe(enqueue_time.elapsed().as_secs_f64());
319                    debug!(?tx_digest, "Input objects available");
320                }
321            _ = self.transaction_cache_read.notify_read_executed_effects_digests(
322                "ExecutionScheduler::notify_read_executed_effects_digests",
323                &digests,
324            ) => {
325                debug!(?tx_digest, "Transaction already executed");
326            }
327        };
328
329        // Send even if the transaction was already executed: the driver must retire its
330        // causal index, and it drops such a transaction before dispatching it. These
331        // sends should only happen briefly after a restart - in steady state, duplicate
332        // enqueues are removed by deduplication.
333        // TODO: Eventually we could fold execution_driver into the scheduler.
334        self.send_transaction_for_execution(&cert, execution_env, enqueue_time);
335    }
336
337    pub fn send_transaction_for_execution(
338        &self,
339        cert: &VerifiedExecutableTransaction,
340        execution_env: ExecutionEnv,
341        enqueue_time: Instant,
342    ) {
343        let pending_cert = PendingCertificate {
344            certificate: cert.clone(),
345            execution_env,
346            stats: PendingCertificateStats {
347                enqueue_time,
348                ready_time: Some(Instant::now()),
349            },
350            executing_guard: Some(ExecutingGuard::new(
351                self.metrics
352                    .transaction_manager_num_executing_certificates
353                    .clone(),
354            )),
355        };
356        let _ = self.tx_ready_certificates.send(pending_cert);
357    }
358
359    /// Groups the funds withdraw reservations of `certs` by accumulator version.
360    fn collect_withdraw_reservations(
361        certs: &[(Schedulable, ExecutionEnv)],
362        epoch_store: &AuthorityPerEpochStore,
363    ) -> BTreeMap<SequenceNumber, Vec<TxFundsWithdraw>> {
364        let mut withdraws = BTreeMap::new();
365        let mut prev_version = None;
366        for (schedulable, env) in certs {
367            let Schedulable::Transaction(cert) = schedulable else {
368                continue;
369            };
370            if !cert.transaction_data().has_funds_withdrawals() {
371                continue;
372            }
373            let tx_withdraws = cert
374                .transaction_data()
375                .process_funds_withdrawals_for_execution(epoch_store.get_chain_identifier());
376            assert!(!tx_withdraws.is_empty());
377            let accumulator_version = env
378                .assigned_versions
379                .accumulator_version()
380                .expect("accumulator_version must be set when there are withdraws");
381            if let Some(prev_version) = prev_version {
382                // Transactions must be in order.
383                assert!(prev_version <= accumulator_version);
384            }
385            prev_version = Some(accumulator_version);
386            withdraws
387                .entry(accumulator_version)
388                .or_insert(Vec::new())
389                .push(TxFundsWithdraw {
390                    tx_digest: *cert.digest(),
391                    reservations: tx_withdraws,
392                });
393        }
394        withdraws
395    }
396
397    /// Submits `reservations` to the funds withdraw scheduler and schedules `certs` as their
398    /// results arrive. `certs` holds only the transactions that survived dedup; results for
399    /// the others are ignored, since those transactions execute from their checkpoint copy.
400    fn schedule_funds_withdraws(
401        &self,
402        reservations: BTreeMap<SequenceNumber, Vec<TxFundsWithdraw>>,
403        certs: Vec<(VerifiedExecutableTransaction, ExecutionEnv)>,
404        epoch_store: &Arc<AuthorityPerEpochStore>,
405    ) {
406        if reservations.is_empty() {
407            return;
408        }
409        let mut receivers = FuturesUnordered::new();
410        {
411            let guard = self.address_funds_withdraw_scheduler.lock();
412            let withdraw_scheduler = guard
413                .as_ref()
414                .expect("Funds withdraw scheduler must be enabled if there are withdraws");
415            for (version, tx_withdraws) in reservations {
416                receivers.extend(withdraw_scheduler.schedule_withdraws(WithdrawReservations {
417                    accumulator_version: version,
418                    withdraws: tx_withdraws,
419                }));
420            }
421            // guard will be dropped here
422        }
423        if certs.is_empty() {
424            return;
425        }
426        let scheduler = self.clone();
427        let epoch_store = epoch_store.clone();
428        spawn_monitored_task!(async move {
429            let mut cert_map = HashMap::new();
430            for (cert, env) in certs {
431                cert_map.insert(*cert.digest(), (cert, env));
432            }
433            while let Some(result) = receivers.next().await {
434                match result {
435                    Ok((tx_digest, status)) => {
436                        let Some((cert, env)) = cert_map.remove(&tx_digest) else {
437                            continue;
438                        };
439                        let env = match status {
440                            ScheduleStatus::InsufficientFunds => {
441                                assert_reachable!("tx cancelled, insufficient funds");
442                                debug!(
443                                    ?tx_digest,
444                                    "Funds withdraw scheduling result: Insufficient funds"
445                                );
446                                env.with_insufficient_funds()
447                            }
448                            ScheduleStatus::SufficientFunds => {
449                                assert_reachable!("tx scheduled, sufficient funds");
450                                debug!(?tx_digest, "Funds withdraw scheduling result: Success");
451                                env
452                            }
453                            ScheduleStatus::SkipSchedule => {
454                                assert_reachable!("tx withdrawal scheduling skipped");
455                                debug!(?tx_digest, "Skip scheduling funds withdraw");
456                                env
457                            }
458                        };
459                        scheduler.spawn_transaction_scheduling(vec![(cert, env)], &epoch_store);
460                    }
461                    Err(e) => {
462                        // A sender drops unsent only if its withdraw is still pending when
463                        // reconfigure replaces the scheduler, but every settlement in an
464                        // epoch executes before the change epoch transaction.
465                        debug_fatal!("Withdraw scheduler stopped: {:?}", e);
466                    }
467                }
468            }
469        });
470    }
471
472    fn schedule_tx_keys(
473        &self,
474        tx_with_keys: Vec<(TransactionKey, ExecutionEnv)>,
475        epoch_store: &Arc<AuthorityPerEpochStore>,
476    ) {
477        if tx_with_keys.is_empty() {
478            return;
479        }
480
481        let scheduler = self.clone();
482        let epoch_store = epoch_store.clone();
483        spawn_monitored_task!(async move {
484            let tx_keys: Vec<_> = tx_with_keys.iter().map(|(key, _)| key).cloned().collect();
485            let digests = epoch_store
486                .notify_read_tx_key_to_digest(&tx_keys)
487                .await
488                .expect("db error");
489            let transactions = scheduler
490                .transaction_cache_read
491                .multi_get_transaction_blocks(&digests)
492                .into_iter()
493                .map(|tx| {
494                    let tx = tx.expect("tx must exist").as_ref().clone();
495                    VerifiedExecutableTransaction::new_system(tx, epoch_store.epoch())
496                })
497                .zip_debug_eq(tx_with_keys.into_iter().map(|(_, env)| env))
498                .collect::<Vec<_>>();
499            scheduler.spawn_transaction_scheduling(transactions, &epoch_store);
500        });
501    }
502
503    /// When we schedule a certificate, it should be impossible for it to have been executed in a
504    /// previous epoch.
505    #[cfg(debug_assertions)]
506    fn assert_cert_not_executed_previous_epochs(&self, cert: &VerifiedExecutableTransaction) {
507        let epoch = cert.epoch();
508        let digest = *cert.digest();
509        let digests = [digest];
510        let executed = self
511            .transaction_cache_read
512            .multi_get_executed_effects(&digests)
513            .pop()
514            .unwrap();
515        // Due to pruning, we may not always have an executed effects for the certificate
516        // even if it was executed. So this is a best-effort check.
517        if let Some(executed) = executed {
518            use sui_types::effects::TransactionEffectsAPI;
519
520            assert_eq!(
521                executed.executed_epoch(),
522                epoch,
523                "Transaction {} was executed in epoch {}, but scheduled again in epoch {}",
524                digest,
525                executed.executed_epoch(),
526                epoch
527            );
528        }
529    }
530}
531
532impl ExecutionScheduler {
533    pub fn enqueue(
534        &self,
535        certs: Vec<(Schedulable, ExecutionEnv)>,
536        epoch_store: &Arc<AuthorityPerEpochStore>,
537    ) {
538        // Deduplicate and assign causal indices for the whole batch first, in enqueue
539        // order, for every unit - including keys whose transactions materialize only
540        // later. A key's transactions must run under the key's index: units enqueued
541        // after it may already be parked waiting for its outputs.
542        //
543        // Withdraw reservations are collected first: the funds scheduler must see every
544        // consensus version, including groups dedup rejects because their checkpoint copy
545        // arrived first, as its balances account for reservations of unsettled versions.
546        let reservations = Self::collect_withdraw_reservations(&certs, epoch_store);
547        let certs = self.causal_admission.dedup_and_assign(certs);
548
549        // schedule all transactions immediately
550        let mut ordinary_txns = Vec::with_capacity(certs.len());
551        let mut tx_with_keys = Vec::new();
552        let mut tx_with_withdraws = Vec::new();
553
554        for (schedulable, env) in certs {
555            match schedulable {
556                Schedulable::Transaction(tx) => {
557                    if tx.transaction_data().has_funds_withdrawals() {
558                        tx_with_withdraws.push((tx, env));
559                    } else {
560                        ordinary_txns.push((tx, env));
561                    }
562                }
563                s @ Schedulable::RandomnessStateUpdate(..) => {
564                    tx_with_keys.push((s.key(), env));
565                }
566                Schedulable::AccumulatorSettlement(_, _) => {
567                    unreachable!("handled by SettlementScheduler");
568                }
569                Schedulable::ConsensusCommitPrologue(_, _, _) => {
570                    // we only use Schedulable::ConsensusCommitPrologue as a temporary placeholder
571                    // during version assignment, by the time we schedule transactions it should be
572                    // converted to Schedulable::Transaction
573                    unreachable!("Schedulable::ConsensusCommitPrologue should not be enqueued");
574                }
575            }
576        }
577
578        self.spawn_transaction_scheduling(ordinary_txns, epoch_store);
579        self.schedule_tx_keys(tx_with_keys, epoch_store);
580        self.schedule_funds_withdraws(reservations, tx_with_withdraws, epoch_store);
581    }
582
583    /// Enqueues digest-carrying transactions. A transaction with a non-digest key (e.g.
584    /// a randomness update) must already have its key resolved in the epoch store:
585    /// consensus enqueued it as a keyed placeholder, and this enqueue is deduplicated
586    /// against that placeholder, so only the resolved key lets it execute.
587    pub fn enqueue_transactions(
588        &self,
589        certs: Vec<(VerifiedExecutableTransaction, ExecutionEnv)>,
590        epoch_store: &Arc<AuthorityPerEpochStore>,
591    ) {
592        // Filter out certificates from wrong epoch.
593        let certs: Vec<_> = certs
594            .into_iter()
595            .filter_map(|cert| {
596                if cert.0.epoch() == epoch_store.epoch() {
597                    #[cfg(debug_assertions)]
598                    self.assert_cert_not_executed_previous_epochs(&cert.0);
599
600                    Some(cert)
601                } else {
602                    debug_fatal!(
603                        "We should never enqueue certificate from wrong epoch. Expected={} Certificate={:?}",
604                        epoch_store.epoch(),
605                        cert.0.epoch()
606                    );
607                    None
608                }
609            })
610            .collect();
611
612        // Precondition: non-digest keys are already resolved (see the doc comment).
613        // Checked here because a violation is silent - the placeholder just never
614        // executes.
615        if mysten_common::in_test_configuration() {
616            for (cert, _) in &certs {
617                if let Some(key) = cert.non_digest_key() {
618                    assert!(
619                        epoch_store.tx_key_to_digest(&key).is_some(),
620                        "enqueued transaction {} with unresolved key {key:?}",
621                        cert.digest()
622                    );
623                }
624            }
625        }
626
627        // Certificates are filtered before index assignment, so dropped ones never
628        // hold an index.
629        let digests: Vec<_> = certs.iter().map(|(cert, _)| *cert.digest()).collect();
630        let executed = self
631            .transaction_cache_read
632            .multi_get_executed_effects_digests(&digests);
633        let mut already_executed_certs_num = 0;
634        let pending_certs: Vec<_> = certs
635            .into_iter()
636            .zip_debug_eq(executed)
637            .filter_map(|((cert, execution_env), executed)| {
638                if executed.is_none() {
639                    Some((cert, execution_env))
640                } else {
641                    already_executed_certs_num += 1;
642                    None
643                }
644            })
645            .collect();
646        let pending_certs = self.causal_admission.dedup_and_assign(pending_certs);
647        self.spawn_transaction_scheduling(pending_certs, epoch_store);
648
649        self.metrics
650            .transaction_manager_num_enqueued_certificates
651            .with_label_values(&["already_executed"])
652            .inc_by(already_executed_certs_num);
653    }
654
655    /// Spawns scheduling for transactions that are past deduplication and index
656    /// assignment: fresh batches, internal re-submissions keeping their original
657    /// index, and settlement transactions - the only units allowed to be index-less
658    /// (the driver admits them unconditionally).
659    pub(crate) fn spawn_transaction_scheduling(
660        &self,
661        certs: Vec<(VerifiedExecutableTransaction, ExecutionEnv)>,
662        epoch_store: &Arc<AuthorityPerEpochStore>,
663    ) {
664        debug_assert!(certs.iter().all(|(cert, env)| {
665            env.causal_index.is_some() || cert.transaction_data().kind().is_accumulator_settle_tx()
666        }));
667        for (cert, execution_env) in certs {
668            let scheduler = self.clone();
669            let epoch_store = epoch_store.clone();
670            spawn_monitored_task!(async move {
671                scheduler
672                    .schedule_transaction(cert, execution_env, &epoch_store)
673                    .await;
674            });
675        }
676    }
677
678    pub fn settle_address_funds(&self, settlement: FundsSettlement) {
679        self.address_funds_withdraw_scheduler
680            .lock()
681            .as_ref()
682            .expect("Funds withdraw scheduler must be enabled if there are settlements")
683            .settle_funds(settlement);
684    }
685
686    /// Reconfigure internal state at epoch start. This resets the funds withdraw scheduler
687    /// to the current accumulator root object version.
688    pub fn reconfigure(
689        &self,
690        new_epoch_store: &Arc<AuthorityPerEpochStore>,
691        account_funds_read: &Arc<dyn AccountFundsRead>,
692    ) {
693        self.causal_admission.check_quiescent_at_epoch_boundary();
694        let address_funds_withdraw_scheduler = Self::initialize_funds_withdraw_scheduler(
695            new_epoch_store,
696            &self.object_cache_read,
697            account_funds_read.clone(),
698            self.funds_withdraw_scheduler_type,
699            &self.address_funds_scheduler_metrics,
700        );
701        let mut guard = self.address_funds_withdraw_scheduler.lock();
702        if let Some(old_scheduler) = guard.as_ref() {
703            old_scheduler.close_epoch();
704        }
705        *guard = address_funds_withdraw_scheduler;
706        drop(guard);
707    }
708
709    pub fn check_execution_overload(
710        &self,
711        overload_config: &AuthorityOverloadConfig,
712        tx_data: &SenderSignedData,
713    ) -> SuiResult {
714        let inflight_queue_len = self.num_pending_certificates();
715        self.overload_tracker
716            .check_execution_overload(overload_config, tx_data, inflight_queue_len)
717    }
718
719    pub fn num_pending_certificates(&self) -> usize {
720        (self
721            .metrics
722            .transaction_manager_num_pending_certificates
723            .get()
724            + self
725                .metrics
726                .transaction_manager_num_executing_certificates
727                .get()) as usize
728    }
729
730    #[cfg(test)]
731    pub async fn check_empty_for_testing(&self) {
732        for _ in 0..500 {
733            if self.num_pending_certificates() == 0 {
734                return;
735            }
736            tokio::time::sleep(std::time::Duration::from_millis(10)).await;
737        }
738        assert_eq!(self.num_pending_certificates(), 0);
739    }
740}
741
742#[cfg(test)]
743mod test {
744    use super::{
745        BarrierDependencyBuilder, ExecutionScheduler, FundsWithdrawSchedulerType,
746        PendingCertificate,
747    };
748    use crate::authority::ExecutionEnv;
749    use crate::authority::shared_object_version_manager::AssignedVersions;
750    use crate::authority::{AuthorityState, authority_tests::init_state_with_objects};
751    use std::collections::BTreeSet;
752    use std::{time::Duration, vec};
753    use sui_macros::sim_test;
754    use sui_test_transaction_builder::TestTransactionBuilder;
755    use sui_types::base_types::{SuiAddress, random_object_ref};
756    use sui_types::executable_transaction::VerifiedExecutableTransaction;
757    use sui_types::object::Owner;
758    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
759    use sui_types::transaction::{
760        SharedObjectMutability, Transaction, TransactionData, TransactionKind, VerifiedTransaction,
761    };
762    use sui_types::{
763        SUI_FRAMEWORK_PACKAGE_ID,
764        base_types::{ObjectID, SequenceNumber},
765        crypto::deterministic_random_account_key,
766        object::Object,
767        transaction::{CallArg, ObjectArg},
768    };
769    use tokio::time::Instant;
770    use tokio::{
771        sync::mpsc::{UnboundedReceiver, error::TryRecvError, unbounded_channel},
772        time::sleep,
773    };
774
775    #[allow(clippy::disallowed_methods)] // allow unbounded_channel()
776    fn make_execution_scheduler(
777        state: &AuthorityState,
778    ) -> (ExecutionScheduler, UnboundedReceiver<PendingCertificate>) {
779        // Create a new execution scheduler instead of reusing the authority's, to examine
780        // execution_scheduler output from rx_ready_certificates.
781        let (tx_ready_certificates, rx_ready_certificates) = unbounded_channel();
782        let registry = prometheus::Registry::new();
783        let execution_scheduler = ExecutionScheduler::new(
784            state.get_object_cache_reader().clone(),
785            state.get_account_funds_read().clone(),
786            state.get_transaction_cache_reader().clone(),
787            tx_ready_certificates,
788            &state.epoch_store_for_testing(),
789            FundsWithdrawSchedulerType::default(),
790            state.metrics.clone(),
791            &registry,
792        );
793
794        (execution_scheduler, rx_ready_certificates)
795    }
796
797    fn make_transaction(gas_object: Object, input: Vec<CallArg>) -> VerifiedExecutableTransaction {
798        // Use fake module, function, package and gas prices since they are irrelevant for testing
799        // execution scheduler.
800        let rgp = 100;
801        let (sender, keypair) = deterministic_random_account_key();
802        let transaction =
803            TestTransactionBuilder::new(sender, gas_object.compute_object_reference(), rgp)
804                .move_call(SUI_FRAMEWORK_PACKAGE_ID, "counter", "assert_value", input)
805                .build_and_sign(&keypair);
806        VerifiedExecutableTransaction::new_system(
807            VerifiedTransaction::new_unchecked(transaction),
808            0,
809        )
810    }
811
812    #[tokio::test(flavor = "current_thread", start_paused = true)]
813    async fn execution_scheduler_basics() {
814        // Initialize an authority state.
815        let (owner, _keypair) = deterministic_random_account_key();
816        let gas_objects: Vec<Object> = (0..10)
817            .map(|_| {
818                let gas_object_id = ObjectID::random();
819                Object::with_id_owner_for_testing(gas_object_id, owner)
820            })
821            .collect();
822        let state = init_state_with_objects(gas_objects.clone()).await;
823
824        // Create a new execution scheduler instead of reusing the authority's, to examine
825        // execution_scheduler output from rx_ready_certificates.
826        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
827        // scheduler should output no transaction.
828        assert!(
829            rx_ready_certificates
830                .try_recv()
831                .is_err_and(|err| err == TryRecvError::Empty)
832        );
833        // scheduler should be empty at the beginning.
834        assert_eq!(execution_scheduler.num_pending_certificates(), 0);
835
836        // Enqueue empty vec should not crash.
837        execution_scheduler.enqueue_transactions(vec![], &state.epoch_store_for_testing());
838        // scheduler should output no transaction.
839        assert!(
840            rx_ready_certificates
841                .try_recv()
842                .is_err_and(|err| err == TryRecvError::Empty)
843        );
844
845        // Enqueue a transaction with existing gas object, empty input.
846        let transaction = make_transaction(gas_objects[0].clone(), vec![]);
847        let tx_start_time = Instant::now();
848        execution_scheduler.enqueue_transactions(
849            vec![(transaction.clone(), ExecutionEnv::new())],
850            &state.epoch_store_for_testing(),
851        );
852        // scheduler should output the transaction eventually.
853        let pending_certificate = rx_ready_certificates.recv().await.unwrap();
854
855        // Tests that pending certificate stats are recorded properly.
856        assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
857        assert!(
858            pending_certificate.stats.ready_time.unwrap() >= pending_certificate.stats.enqueue_time
859        );
860
861        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
862
863        // Predent we have just executed the transaction.
864        drop(pending_certificate);
865
866        // scheduler should be empty.
867        execution_scheduler.check_empty_for_testing().await;
868
869        // Enqueue a transaction with a new gas object, empty input.
870        let gas_object_new = Object::with_id_owner_version_for_testing(
871            ObjectID::random(),
872            0.into(),
873            Owner::AddressOwner(owner),
874        );
875        let transaction = make_transaction(gas_object_new.clone(), vec![]);
876        let tx_start_time = Instant::now();
877        execution_scheduler.enqueue_transactions(
878            vec![(transaction.clone(), ExecutionEnv::new())],
879            &state.epoch_store_for_testing(),
880        );
881        // scheduler should output no transaction yet.
882        sleep(Duration::from_secs(1)).await;
883        assert!(
884            rx_ready_certificates
885                .try_recv()
886                .is_err_and(|err| err == TryRecvError::Empty)
887        );
888
889        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
890
891        // Duplicated enqueue is allowed.
892        execution_scheduler.enqueue_transactions(
893            vec![(transaction.clone(), ExecutionEnv::new())],
894            &state.epoch_store_for_testing(),
895        );
896        sleep(Duration::from_secs(1)).await;
897        assert!(
898            rx_ready_certificates
899                .try_recv()
900                .is_err_and(|err| err == TryRecvError::Empty)
901        );
902
903        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
904
905        // Notify scheduler about availability of the gas object.
906        state
907            .get_cache_writer()
908            .write_object_entry_for_test(gas_object_new);
909        // scheduler should output the transaction eventually.
910        // We will see both the original and the duplicated transaction.
911        let pending_certificate = rx_ready_certificates.recv().await.unwrap();
912        let pending_certificate2 = rx_ready_certificates.recv().await.unwrap();
913        assert_eq!(
914            pending_certificate.certificate.digest(),
915            pending_certificate2.certificate.digest()
916        );
917
918        // Tests that pending certificate stats are recorded properly. The ready time should be
919        // 2 seconds apart from the enqueue time.
920        assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
921        assert!(
922            pending_certificate.stats.ready_time.unwrap() - pending_certificate.stats.enqueue_time
923                >= Duration::from_secs(2)
924        );
925
926        // Predent we have just executed the transaction.
927        drop(pending_certificate);
928        drop(pending_certificate2);
929
930        // scheduler should be empty at the end.
931        execution_scheduler.check_empty_for_testing().await;
932    }
933
934    // Tests when objects become available, correct set of transactions can be sent to execute.
935    // Specifically, we have following setup,
936    //         shared_object     shared_object_2
937    //       /    |    \     \    /
938    //    tx_0  tx_1  tx_2    tx_3
939    //     r      r     w      r
940    // And when shared_object is available, tx_0, tx_1, and tx_2 can be executed. And when
941    // shared_object_2 becomes available, tx_3 can be executed.
942    #[sim_test]
943    async fn execution_scheduler_object_dependency() {
944        telemetry_subscribers::init_for_testing();
945        // Initialize an authority state, with gas objects and a shared object.
946        let (owner, _keypair) = deterministic_random_account_key();
947        let gas_objects: Vec<Object> = (0..10)
948            .map(|_| {
949                let gas_object_id = ObjectID::random();
950                Object::with_id_owner_for_testing(gas_object_id, owner)
951            })
952            .collect();
953        let shared_object = Object::shared_for_testing();
954        let initial_shared_version = shared_object.owner().start_version().unwrap();
955        let shared_object_2 = Object::shared_for_testing();
956        let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
957
958        let state = init_state_with_objects(
959            [
960                gas_objects.clone(),
961                vec![shared_object.clone(), shared_object_2.clone()],
962            ]
963            .concat(),
964        )
965        .await;
966
967        // Create a new execution scheduler instead of reusing the authority's, to examine
968        // execution_scheduler output from rx_ready_certificates.
969        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
970        // scheduler should output no transaction.
971        assert!(rx_ready_certificates.try_recv().is_err());
972
973        // Enqueue two transactions with the same shared object input in read-only mode.
974        let shared_version = 1000.into();
975        let shared_object_arg_read = ObjectArg::SharedObject {
976            id: shared_object.id(),
977            initial_shared_version,
978            mutability: SharedObjectMutability::Immutable,
979        };
980        let transaction_read_0 = make_transaction(
981            gas_objects[0].clone(),
982            vec![CallArg::Object(shared_object_arg_read)],
983        );
984        let transaction_read_1 = make_transaction(
985            gas_objects[1].clone(),
986            vec![CallArg::Object(shared_object_arg_read)],
987        );
988        let tx_read_0_assigned_versions = vec![(
989            (
990                shared_object.id(),
991                shared_object.owner().start_version().unwrap(),
992            ),
993            shared_version,
994        )];
995        let tx_read_1_assigned_versions = vec![(
996            (
997                shared_object.id(),
998                shared_object.owner().start_version().unwrap(),
999            ),
1000            shared_version,
1001        )];
1002
1003        // Enqueue one transaction with the same shared object in mutable mode.
1004        let shared_object_arg_default = ObjectArg::SharedObject {
1005            id: shared_object.id(),
1006            initial_shared_version,
1007            mutability: SharedObjectMutability::Mutable,
1008        };
1009        let transaction_default = make_transaction(
1010            gas_objects[2].clone(),
1011            vec![CallArg::Object(shared_object_arg_default)],
1012        );
1013        let tx_default_assigned_versions = vec![(
1014            (
1015                shared_object.id(),
1016                shared_object.owner().start_version().unwrap(),
1017            ),
1018            shared_version,
1019        )];
1020
1021        // Enqueue one transaction with two readonly shared object inputs, `shared_object` and `shared_object_2`.
1022        let shared_version_2 = 1000.into();
1023        let shared_object_arg_read_2 = ObjectArg::SharedObject {
1024            id: shared_object_2.id(),
1025            initial_shared_version: initial_shared_version_2,
1026            mutability: SharedObjectMutability::Immutable,
1027        };
1028        let transaction_read_2 = make_transaction(
1029            gas_objects[3].clone(),
1030            vec![
1031                CallArg::Object(shared_object_arg_default),
1032                CallArg::Object(shared_object_arg_read_2),
1033            ],
1034        );
1035        let tx_read_2_assigned_versions = vec![
1036            (
1037                (
1038                    shared_object.id(),
1039                    shared_object.owner().start_version().unwrap(),
1040                ),
1041                shared_version,
1042            ),
1043            (
1044                (
1045                    shared_object_2.id(),
1046                    shared_object_2.owner().start_version().unwrap(),
1047                ),
1048                shared_version_2,
1049            ),
1050        ];
1051
1052        execution_scheduler.enqueue_transactions(
1053            vec![
1054                (
1055                    transaction_read_0.clone(),
1056                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1057                        tx_read_0_assigned_versions,
1058                        None,
1059                    )),
1060                ),
1061                (
1062                    transaction_read_1.clone(),
1063                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1064                        tx_read_1_assigned_versions,
1065                        None,
1066                    )),
1067                ),
1068                (
1069                    transaction_default.clone(),
1070                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1071                        tx_default_assigned_versions,
1072                        None,
1073                    )),
1074                ),
1075                (
1076                    transaction_read_2.clone(),
1077                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1078                        tx_read_2_assigned_versions,
1079                        None,
1080                    )),
1081                ),
1082            ],
1083            &state.epoch_store_for_testing(),
1084        );
1085
1086        // scheduler should output no transaction yet.
1087        sleep(Duration::from_secs(1)).await;
1088        assert!(rx_ready_certificates.try_recv().is_err());
1089
1090        assert_eq!(execution_scheduler.num_pending_certificates(), 4);
1091
1092        // Notify scheduler about availability of the first shared object.
1093        let mut new_shared_object = shared_object.clone();
1094        new_shared_object
1095            .data
1096            .try_as_move_mut()
1097            .unwrap()
1098            .increment_version_to(shared_version_2);
1099        state
1100            .get_cache_writer()
1101            .write_object_entry_for_test(new_shared_object);
1102
1103        // scheduler should output the 3 transactions that are only waiting for this object.
1104        let tx_0 = rx_ready_certificates.recv().await.unwrap().certificate;
1105        let tx_1 = rx_ready_certificates.recv().await.unwrap().certificate;
1106        let tx_2 = rx_ready_certificates.recv().await.unwrap().certificate;
1107        {
1108            let mut want_digests = vec![
1109                transaction_read_0.digest(),
1110                transaction_read_1.digest(),
1111                transaction_default.digest(),
1112            ];
1113            want_digests.sort();
1114            let mut got_digests = vec![tx_0.digest(), tx_1.digest(), tx_2.digest()];
1115            got_digests.sort();
1116            assert_eq!(want_digests, got_digests);
1117        }
1118
1119        sleep(Duration::from_secs(1)).await;
1120        assert!(rx_ready_certificates.try_recv().is_err());
1121
1122        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1123
1124        // Make shared_object_2 available.
1125        let mut new_shared_object_2 = shared_object_2.clone();
1126        new_shared_object_2
1127            .data
1128            .try_as_move_mut()
1129            .unwrap()
1130            .increment_version_to(shared_version_2);
1131        state
1132            .get_cache_writer()
1133            .write_object_entry_for_test(new_shared_object_2);
1134
1135        // Now, the transaction waiting for both shared objects can be executed.
1136        let tx_3 = rx_ready_certificates.recv().await.unwrap().certificate;
1137        assert_eq!(transaction_read_2.digest(), tx_3.digest());
1138
1139        sleep(Duration::from_secs(1)).await;
1140        assert!(rx_ready_certificates.try_recv().is_err());
1141
1142        execution_scheduler.check_empty_for_testing().await;
1143    }
1144
1145    #[sim_test]
1146    async fn execution_scheduler_receiving_notify_commit() {
1147        telemetry_subscribers::init_for_testing();
1148        // Initialize an authority state.
1149        let (owner, _keypair) = deterministic_random_account_key();
1150        let gas_objects: Vec<Object> = (0..10)
1151            .map(|_| {
1152                let gas_object_id = ObjectID::random();
1153                Object::with_id_owner_for_testing(gas_object_id, owner)
1154            })
1155            .collect();
1156        let state = init_state_with_objects(gas_objects.clone()).await;
1157
1158        // Create a new execution scheduler instead of reusing the authority's, to examine
1159        // execution_scheduler output from rx_ready_certificates.
1160        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1161        // scheduler should output no transaction.
1162        assert!(rx_ready_certificates.try_recv().is_err());
1163        // scheduler should be empty at the beginning.
1164        execution_scheduler.check_empty_for_testing().await;
1165
1166        let obj_id = ObjectID::random();
1167        let object_arguments: Vec<_> = (0..10)
1168            .map(|i| {
1169                let object = Object::with_id_owner_version_for_testing(
1170                    obj_id,
1171                    i.into(),
1172                    Owner::AddressOwner(owner),
1173                );
1174                // Every other transaction receives the object, and we create a run of multiple receives in
1175                // a row at the beginning to test that the scheduler doesn't get stuck in either configuration of:
1176                // ImmOrOwnedObject => Receiving,
1177                // Receiving => Receiving
1178                // Receiving => ImmOrOwnedObject
1179                // ImmOrOwnedObject => ImmOrOwnedObject is already tested as the default case on mainnet.
1180                let object_arg = if i % 2 == 0 || i == 3 {
1181                    ObjectArg::Receiving(object.compute_object_reference())
1182                } else {
1183                    ObjectArg::ImmOrOwnedObject(object.compute_object_reference())
1184                };
1185                let txn =
1186                    make_transaction(gas_objects[0].clone(), vec![CallArg::Object(object_arg)]);
1187                (object, txn)
1188            })
1189            .collect();
1190
1191        for (i, (_, txn)) in object_arguments.iter().enumerate() {
1192            // scheduler should output no transaction yet since waiting on receiving object or
1193            // ImmOrOwnedObject input.
1194            execution_scheduler.enqueue_transactions(
1195                vec![(txn.clone(), ExecutionEnv::new())],
1196                &state.epoch_store_for_testing(),
1197            );
1198            sleep(Duration::from_secs(1)).await;
1199            assert!(rx_ready_certificates.try_recv().is_err());
1200            assert_eq!(execution_scheduler.num_pending_certificates(), i + 1);
1201        }
1202
1203        // Now start to unravel the transactions by notifying that each subsequent
1204        // transaction has been processed.
1205        let len = object_arguments.len();
1206        for (i, (object, txn)) in object_arguments.into_iter().enumerate() {
1207            // Mark the object as available.
1208            // We should now eventually see the transaction as ready.
1209            state
1210                .get_cache_writer()
1211                .write_object_entry_for_test(object.clone());
1212
1213            // scheduler should output the transaction eventually now that the receiving object has become
1214            // available.
1215            rx_ready_certificates.recv().await.unwrap();
1216
1217            // Only one transaction at a time should become available though. So if we try to get
1218            // another one it should fail.
1219            sleep(Duration::from_secs(1)).await;
1220            assert!(rx_ready_certificates.try_recv().is_err());
1221
1222            // Notify the scheduler that the transaction has been processed.
1223            drop(txn);
1224
1225            // scheduler should now output another transaction to run since it the next version of that object
1226            // has become available.
1227            assert_eq!(execution_scheduler.num_pending_certificates(), len - i - 1);
1228        }
1229
1230        // After everything scheduler should be empty.
1231        execution_scheduler.check_empty_for_testing().await;
1232    }
1233
1234    #[sim_test]
1235    async fn execution_scheduler_receiving_object_ready_notifications() {
1236        telemetry_subscribers::init_for_testing();
1237        // Initialize an authority state.
1238        let (owner, _keypair) = deterministic_random_account_key();
1239        let gas_objects: Vec<Object> = (0..10)
1240            .map(|_| {
1241                let gas_object_id = ObjectID::random();
1242                Object::with_id_owner_for_testing(gas_object_id, owner)
1243            })
1244            .collect();
1245        let state = init_state_with_objects(gas_objects.clone()).await;
1246
1247        // Create a new execution scheduler instead of reusing the authority's, to examine
1248        // execution_scheduler output from rx_ready_certificates.
1249        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1250        // scheduler should output no transaction.
1251        assert!(rx_ready_certificates.try_recv().is_err());
1252        // scheduler should be empty at the beginning.
1253        execution_scheduler.check_empty_for_testing().await;
1254
1255        let obj_id = ObjectID::random();
1256        let receiving_object_new0 =
1257            Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1258        let receiving_object_new1 =
1259            Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1260        let receiving_object_arg0 =
1261            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1262        let receive_object_transaction0 = make_transaction(
1263            gas_objects[0].clone(),
1264            vec![CallArg::Object(receiving_object_arg0)],
1265        );
1266
1267        let receiving_object_arg1 =
1268            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1269        let receive_object_transaction1 = make_transaction(
1270            gas_objects[0].clone(),
1271            vec![CallArg::Object(receiving_object_arg1)],
1272        );
1273
1274        // scheduler should output no transaction yet since waiting on receiving object.
1275        execution_scheduler.enqueue_transactions(
1276            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1277            &state.epoch_store_for_testing(),
1278        );
1279        sleep(Duration::from_secs(1)).await;
1280        assert!(rx_ready_certificates.try_recv().is_err());
1281        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1282
1283        // scheduler should output no transaction yet since waiting on receiving object.
1284        execution_scheduler.enqueue_transactions(
1285            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1286            &state.epoch_store_for_testing(),
1287        );
1288        sleep(Duration::from_secs(1)).await;
1289        assert!(rx_ready_certificates.try_recv().is_err());
1290        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1291
1292        // Duplicate enqueue of receiving object is allowed.
1293        execution_scheduler.enqueue_transactions(
1294            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1295            &state.epoch_store_for_testing(),
1296        );
1297        sleep(Duration::from_secs(1)).await;
1298        assert!(rx_ready_certificates.try_recv().is_err());
1299        assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1300
1301        // Notify scheduler that the receiving object 0 is available.
1302        state
1303            .get_cache_writer()
1304            .write_object_entry_for_test(receiving_object_new0.clone());
1305
1306        // scheduler should output the transaction eventually now that the receiving object has become
1307        // available.
1308        rx_ready_certificates.recv().await.unwrap();
1309
1310        // Notify scheduler that the receiving object 0 is available.
1311        state
1312            .get_cache_writer()
1313            .write_object_entry_for_test(receiving_object_new1.clone());
1314
1315        // scheduler should output the transaction eventually now that the receiving object has become
1316        // available.
1317        rx_ready_certificates.recv().await.unwrap();
1318    }
1319
1320    #[sim_test]
1321    async fn execution_scheduler_receiving_object_ready_notifications_multiple_of_same_receiving() {
1322        telemetry_subscribers::init_for_testing();
1323        // Initialize an authority state.
1324        let (owner, _keypair) = deterministic_random_account_key();
1325        let gas_objects: Vec<Object> = (0..10)
1326            .map(|_| {
1327                let gas_object_id = ObjectID::random();
1328                Object::with_id_owner_for_testing(gas_object_id, owner)
1329            })
1330            .collect();
1331        let state = init_state_with_objects(gas_objects.clone()).await;
1332
1333        // Create a new execution scheduler instead of reusing the authority's, to examine
1334        // execution_scheduler output from rx_ready_certificates.
1335        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1336        // scheduler should output no transaction.
1337        assert!(rx_ready_certificates.try_recv().is_err());
1338        // scheduler should be empty at the beginning.
1339        execution_scheduler.check_empty_for_testing().await;
1340
1341        let obj_id = ObjectID::random();
1342        let receiving_object_new0 =
1343            Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1344        let receiving_object_new1 =
1345            Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1346        let receiving_object_arg0 =
1347            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1348        let receive_object_transaction0 = make_transaction(
1349            gas_objects[0].clone(),
1350            vec![CallArg::Object(receiving_object_arg0)],
1351        );
1352
1353        let receive_object_transaction01 = make_transaction(
1354            gas_objects[1].clone(),
1355            vec![CallArg::Object(receiving_object_arg0)],
1356        );
1357
1358        let receiving_object_arg1 =
1359            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1360        let receive_object_transaction1 = make_transaction(
1361            gas_objects[0].clone(),
1362            vec![CallArg::Object(receiving_object_arg1)],
1363        );
1364
1365        // Enqueuing a transaction with a receiving object that is available at the time it is enqueued
1366        // should become immediately available.
1367        let gas_receiving_arg = ObjectArg::Receiving(gas_objects[3].compute_object_reference());
1368        let tx1 = make_transaction(
1369            gas_objects[0].clone(),
1370            vec![CallArg::Object(gas_receiving_arg)],
1371        );
1372
1373        // scheduler should output no transaction yet since waiting on receiving object.
1374        execution_scheduler.enqueue_transactions(
1375            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1376            &state.epoch_store_for_testing(),
1377        );
1378        sleep(Duration::from_secs(1)).await;
1379        assert!(rx_ready_certificates.try_recv().is_err());
1380        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1381
1382        // scheduler should output no transaction yet since waiting on receiving object.
1383        execution_scheduler.enqueue_transactions(
1384            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1385            &state.epoch_store_for_testing(),
1386        );
1387        sleep(Duration::from_secs(1)).await;
1388        assert!(rx_ready_certificates.try_recv().is_err());
1389        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1390
1391        // Different transaction with a duplicate receiving object reference is allowed.
1392        // Both transaction's will be outputted once the receiving object is available.
1393        execution_scheduler.enqueue_transactions(
1394            vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1395            &state.epoch_store_for_testing(),
1396        );
1397        sleep(Duration::from_secs(1)).await;
1398        assert!(rx_ready_certificates.try_recv().is_err());
1399        assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1400
1401        // Notify scheduler that the receiving object 0 is available.
1402        state
1403            .get_cache_writer()
1404            .write_object_entry_for_test(receiving_object_new0.clone());
1405
1406        // scheduler should output both transactions depending on the receiving object now that the
1407        // transaction's receiving object has become available.
1408        rx_ready_certificates.recv().await.unwrap();
1409
1410        rx_ready_certificates.recv().await.unwrap();
1411
1412        // Only two transactions that were dependent on the receiving object should be output.
1413        assert!(rx_ready_certificates.try_recv().is_err());
1414
1415        // Enqueue a transaction with a receiving object that is available at the time it is enqueued.
1416        // This should be immediately available.
1417        execution_scheduler.enqueue_transactions(
1418            vec![(tx1.clone(), ExecutionEnv::new())],
1419            &state.epoch_store_for_testing(),
1420        );
1421        sleep(Duration::from_secs(1)).await;
1422        rx_ready_certificates.recv().await.unwrap();
1423
1424        // Notify scheduler that the receiving object 0 is available.
1425        state
1426            .get_cache_writer()
1427            .write_object_entry_for_test(receiving_object_new1.clone());
1428
1429        // scheduler should output the transaction eventually now that the receiving object has become
1430        // available.
1431        rx_ready_certificates.recv().await.unwrap();
1432    }
1433
1434    #[sim_test]
1435    async fn execution_scheduler_receiving_object_ready_if_current_version_greater() {
1436        telemetry_subscribers::init_for_testing();
1437        // Initialize an authority state.
1438        let (owner, _keypair) = deterministic_random_account_key();
1439        let mut gas_objects: Vec<Object> = (0..10)
1440            .map(|_| {
1441                let gas_object_id = ObjectID::random();
1442                Object::with_id_owner_for_testing(gas_object_id, owner)
1443            })
1444            .collect();
1445        let receiving_object = Object::with_id_owner_version_for_testing(
1446            ObjectID::random(),
1447            10.into(),
1448            Owner::AddressOwner(owner),
1449        );
1450        gas_objects.push(receiving_object.clone());
1451        let state = init_state_with_objects(gas_objects.clone()).await;
1452
1453        // Create a new execution scheduler instead of reusing the authority's, to examine
1454        // execution_scheduler output from rx_ready_certificates.
1455        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1456        // scheduler should output no transaction.
1457        assert!(rx_ready_certificates.try_recv().is_err());
1458        // scheduler should be empty at the beginning.
1459        execution_scheduler.check_empty_for_testing().await;
1460
1461        let receiving_object_new0 = Object::with_id_owner_version_for_testing(
1462            receiving_object.id(),
1463            0.into(),
1464            Owner::AddressOwner(owner),
1465        );
1466        let receiving_object_new1 = Object::with_id_owner_version_for_testing(
1467            receiving_object.id(),
1468            1.into(),
1469            Owner::AddressOwner(owner),
1470        );
1471        let receiving_object_arg0 =
1472            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1473        let receive_object_transaction0 = make_transaction(
1474            gas_objects[0].clone(),
1475            vec![CallArg::Object(receiving_object_arg0)],
1476        );
1477
1478        let receive_object_transaction01 = make_transaction(
1479            gas_objects[1].clone(),
1480            vec![CallArg::Object(receiving_object_arg0)],
1481        );
1482
1483        let receiving_object_arg1 =
1484            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1485        let receive_object_transaction1 = make_transaction(
1486            gas_objects[0].clone(),
1487            vec![CallArg::Object(receiving_object_arg1)],
1488        );
1489
1490        // scheduler should output no transaction yet since waiting on receiving object.
1491        execution_scheduler.enqueue_transactions(
1492            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1493            &state.epoch_store_for_testing(),
1494        );
1495        execution_scheduler.enqueue_transactions(
1496            vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1497            &state.epoch_store_for_testing(),
1498        );
1499        execution_scheduler.enqueue_transactions(
1500            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1501            &state.epoch_store_for_testing(),
1502        );
1503        sleep(Duration::from_secs(1)).await;
1504        rx_ready_certificates.recv().await.unwrap();
1505        rx_ready_certificates.recv().await.unwrap();
1506        rx_ready_certificates.recv().await.unwrap();
1507        assert!(rx_ready_certificates.try_recv().is_err());
1508    }
1509
1510    // Tests transaction cancellation logic in execution scheduler. Mainly tests that for cancelled transaction,
1511    // execution scheduler only waits for all non-shared objects to be available before outputting the transaction.
1512    #[sim_test]
1513    async fn execution_scheduler_with_cancelled_transactions() {
1514        // Initialize an authority state, with gas objects and 3 shared objects.
1515        let (owner, _keypair) = deterministic_random_account_key();
1516        let gas_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1517        let shared_object_1 = Object::shared_for_testing();
1518        let initial_shared_version_1 = shared_object_1.owner().start_version().unwrap();
1519        let shared_object_2 = Object::shared_for_testing();
1520        let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
1521        let owned_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1522
1523        let state = init_state_with_objects(vec![
1524            gas_object.clone(),
1525            shared_object_1.clone(),
1526            shared_object_2.clone(),
1527            owned_object.clone(),
1528        ])
1529        .await;
1530
1531        // Create a new execution scheduler instead of reusing the authority's, to examine
1532        // execution_scheduler output from rx_ready_certificates.
1533        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1534        // scheduler should output no transaction.
1535        assert!(rx_ready_certificates.try_recv().is_err());
1536
1537        // Enqueue one transaction with 2 shared object inputs and 1 owned input.
1538        let shared_object_arg_1 = ObjectArg::SharedObject {
1539            id: shared_object_1.id(),
1540            initial_shared_version: initial_shared_version_1,
1541            mutability: SharedObjectMutability::Mutable,
1542        };
1543        let shared_object_arg_2 = ObjectArg::SharedObject {
1544            id: shared_object_2.id(),
1545            initial_shared_version: initial_shared_version_2,
1546            mutability: SharedObjectMutability::Mutable,
1547        };
1548
1549        // Changes the desired owned object version to a higher version. We will make it available later.
1550        let owned_version = 2000.into();
1551        let mut owned_ref = owned_object.compute_object_reference();
1552        owned_ref.1 = owned_version;
1553        let owned_object_arg = ObjectArg::ImmOrOwnedObject(owned_ref);
1554
1555        let cancelled_transaction = make_transaction(
1556            gas_object.clone(),
1557            vec![
1558                CallArg::Object(shared_object_arg_1),
1559                CallArg::Object(shared_object_arg_2),
1560                CallArg::Object(owned_object_arg),
1561            ],
1562        );
1563        let assigned_versions = vec![
1564            (
1565                (
1566                    shared_object_1.id(),
1567                    shared_object_1.owner().start_version().unwrap(),
1568                ),
1569                SequenceNumber::CANCELLED_READ,
1570            ),
1571            (
1572                (
1573                    shared_object_2.id(),
1574                    shared_object_2.owner().start_version().unwrap(),
1575                ),
1576                SequenceNumber::CONGESTED,
1577            ),
1578        ];
1579
1580        execution_scheduler.enqueue_transactions(
1581            vec![(
1582                cancelled_transaction.clone(),
1583                ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1584                    assigned_versions,
1585                    None,
1586                )),
1587            )],
1588            &state.epoch_store_for_testing(),
1589        );
1590
1591        // scheduler should output no transaction yet.
1592        sleep(Duration::from_secs(1)).await;
1593        assert!(rx_ready_certificates.try_recv().is_err());
1594
1595        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1596
1597        // Notify scheduler about availability of the owned object.
1598        let mut new_owned_object = owned_object.clone();
1599        new_owned_object
1600            .data
1601            .try_as_move_mut()
1602            .unwrap()
1603            .increment_version_to(owned_version);
1604        state
1605            .get_cache_writer()
1606            .write_object_entry_for_test(new_owned_object);
1607
1608        // scheduler should output the transaction as soon as the owned object is available.
1609        let available_txn = rx_ready_certificates.recv().await.unwrap().certificate;
1610        assert_eq!(available_txn.digest(), cancelled_transaction.digest());
1611
1612        sleep(Duration::from_secs(1)).await;
1613        assert!(rx_ready_certificates.try_recv().is_err());
1614
1615        execution_scheduler.check_empty_for_testing().await;
1616    }
1617
1618    #[test]
1619    fn test_barrier_dependency_builder() {
1620        let make_transaction = |non_exclusive_writes: Vec<u32>, exclusive_writes: Vec<u32>| {
1621            assert!(
1622                non_exclusive_writes
1623                    .iter()
1624                    .all(|id| !exclusive_writes.contains(id))
1625            );
1626            assert!(
1627                exclusive_writes
1628                    .iter()
1629                    .all(|id| !non_exclusive_writes.contains(id))
1630            );
1631
1632            let non_exclusive_writes = non_exclusive_writes
1633                .into_iter()
1634                .map(|id| ObjectID::from_single_byte(id as u8));
1635            let exclusive_writes = exclusive_writes
1636                .into_iter()
1637                .map(|id| ObjectID::from_single_byte(id as u8));
1638            let mut builder = ProgrammableTransactionBuilder::new();
1639            for non_exclusive_write in non_exclusive_writes {
1640                builder
1641                    .obj(ObjectArg::SharedObject {
1642                        id: non_exclusive_write,
1643                        initial_shared_version: SequenceNumber::new(),
1644                        mutability: SharedObjectMutability::NonExclusiveWrite,
1645                    })
1646                    .unwrap();
1647            }
1648
1649            for exclusive_write in exclusive_writes {
1650                builder
1651                    .obj(ObjectArg::SharedObject {
1652                        id: exclusive_write,
1653                        initial_shared_version: SequenceNumber::new(),
1654                        mutability: SharedObjectMutability::Mutable,
1655                    })
1656                    .unwrap();
1657            }
1658
1659            let tx = TransactionKind::ProgrammableTransaction(builder.finish());
1660            let tx_data =
1661                TransactionData::new(tx, SuiAddress::default(), random_object_ref(), 1, 1);
1662            Transaction::from_data_and_signer(tx_data, vec![])
1663        };
1664
1665        // One non-exclusive write, one exclusive write.
1666        {
1667            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1668            let tx1 = make_transaction(vec![1], vec![]);
1669            let tx2 = make_transaction(vec![], vec![1]);
1670
1671            let tx1_deps =
1672                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1673            let tx2_deps =
1674                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1675            assert!(tx1_deps.is_empty());
1676            assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1677        }
1678
1679        // One transaction has non-exclusive writes to two different objects, and then becomes
1680        // a dependency of two barriers
1681        {
1682            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1683            let tx1 = make_transaction(vec![1, 2], vec![]);
1684            let tx2 = make_transaction(vec![], vec![1]);
1685            let tx3 = make_transaction(vec![], vec![2]);
1686
1687            let tx1_deps =
1688                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1689            let tx2_deps =
1690                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1691            let tx3_deps =
1692                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1693            assert!(tx1_deps.is_empty());
1694            assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1695            assert_eq!(Vec::from_iter(tx3_deps), vec![*tx1.digest()]);
1696        }
1697
1698        // Ensure multiple-object dependences are merged
1699        {
1700            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1701            let tx1 = make_transaction(vec![1], vec![]);
1702            let tx2 = make_transaction(vec![2], vec![]);
1703            let tx3 = make_transaction(vec![], vec![1, 2]);
1704
1705            let tx1_deps =
1706                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1707            let tx2_deps =
1708                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1709            let tx3_deps =
1710                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1711            assert!(tx1_deps.is_empty());
1712            assert!(tx2_deps.is_empty());
1713            assert_eq!(tx3_deps, BTreeSet::from([*tx1.digest(), *tx2.digest()]));
1714        }
1715
1716        // Ensure dependency state is cleared
1717        {
1718            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1719            let tx1 = make_transaction(vec![1], vec![]);
1720            let tx2 = make_transaction(vec![], vec![1]);
1721            let tx3 = make_transaction(vec![], vec![1]);
1722
1723            let tx1_deps =
1724                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1725            let tx2_deps =
1726                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1727            let tx3_deps =
1728                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1729            assert!(tx1_deps.is_empty());
1730            assert_eq!(tx2_deps, BTreeSet::from([*tx1.digest()]));
1731            assert!(tx3_deps.is_empty());
1732        }
1733    }
1734}