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_test_transaction_builder::TestTransactionBuilder;
754    use sui_types::base_types::{SuiAddress, random_object_ref};
755    use sui_types::executable_transaction::VerifiedExecutableTransaction;
756    use sui_types::object::Owner;
757    use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
758    use sui_types::transaction::{
759        SharedObjectMutability, Transaction, TransactionData, TransactionKind, VerifiedTransaction,
760    };
761    use sui_types::{
762        SUI_FRAMEWORK_PACKAGE_ID,
763        base_types::{ObjectID, SequenceNumber},
764        crypto::deterministic_random_account_key,
765        object::Object,
766        transaction::{CallArg, ObjectArg},
767    };
768    use tokio::time::Instant;
769    use tokio::{
770        sync::mpsc::{UnboundedReceiver, error::TryRecvError, unbounded_channel},
771        time::sleep,
772    };
773
774    #[allow(clippy::disallowed_methods)] // allow unbounded_channel()
775    fn make_execution_scheduler(
776        state: &AuthorityState,
777    ) -> (ExecutionScheduler, UnboundedReceiver<PendingCertificate>) {
778        // Create a new execution scheduler instead of reusing the authority's, to examine
779        // execution_scheduler output from rx_ready_certificates.
780        let (tx_ready_certificates, rx_ready_certificates) = unbounded_channel();
781        let registry = prometheus::Registry::new();
782        let execution_scheduler = ExecutionScheduler::new(
783            state.get_object_cache_reader().clone(),
784            state.get_account_funds_read().clone(),
785            state.get_transaction_cache_reader().clone(),
786            tx_ready_certificates,
787            &state.epoch_store_for_testing(),
788            FundsWithdrawSchedulerType::default(),
789            state.metrics.clone(),
790            &registry,
791        );
792
793        (execution_scheduler, rx_ready_certificates)
794    }
795
796    fn make_transaction(gas_object: Object, input: Vec<CallArg>) -> VerifiedExecutableTransaction {
797        // Use fake module, function, package and gas prices since they are irrelevant for testing
798        // execution scheduler.
799        let rgp = 100;
800        let (sender, keypair) = deterministic_random_account_key();
801        let transaction =
802            TestTransactionBuilder::new(sender, gas_object.compute_object_reference(), rgp)
803                .move_call(SUI_FRAMEWORK_PACKAGE_ID, "counter", "assert_value", input)
804                .build_and_sign(&keypair);
805        VerifiedExecutableTransaction::new_system(
806            VerifiedTransaction::new_unchecked(transaction),
807            0,
808        )
809    }
810
811    #[tokio::test(flavor = "current_thread", start_paused = true)]
812    async fn execution_scheduler_basics() {
813        // Initialize an authority state.
814        let (owner, _keypair) = deterministic_random_account_key();
815        let gas_objects: Vec<Object> = (0..10)
816            .map(|_| {
817                let gas_object_id = ObjectID::random();
818                Object::with_id_owner_for_testing(gas_object_id, owner)
819            })
820            .collect();
821        let state = init_state_with_objects(gas_objects.clone()).await;
822
823        // Create a new execution scheduler instead of reusing the authority's, to examine
824        // execution_scheduler output from rx_ready_certificates.
825        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
826        // scheduler should output no transaction.
827        assert!(
828            rx_ready_certificates
829                .try_recv()
830                .is_err_and(|err| err == TryRecvError::Empty)
831        );
832        // scheduler should be empty at the beginning.
833        assert_eq!(execution_scheduler.num_pending_certificates(), 0);
834
835        // Enqueue empty vec should not crash.
836        execution_scheduler.enqueue_transactions(vec![], &state.epoch_store_for_testing());
837        // scheduler should output no transaction.
838        assert!(
839            rx_ready_certificates
840                .try_recv()
841                .is_err_and(|err| err == TryRecvError::Empty)
842        );
843
844        // Enqueue a transaction with existing gas object, empty input.
845        let transaction = make_transaction(gas_objects[0].clone(), vec![]);
846        let tx_start_time = Instant::now();
847        execution_scheduler.enqueue_transactions(
848            vec![(transaction.clone(), ExecutionEnv::new())],
849            &state.epoch_store_for_testing(),
850        );
851        // scheduler should output the transaction eventually.
852        let pending_certificate = rx_ready_certificates.recv().await.unwrap();
853
854        // Tests that pending certificate stats are recorded properly.
855        assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
856        assert!(
857            pending_certificate.stats.ready_time.unwrap() >= pending_certificate.stats.enqueue_time
858        );
859
860        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
861
862        // Predent we have just executed the transaction.
863        drop(pending_certificate);
864
865        // scheduler should be empty.
866        execution_scheduler.check_empty_for_testing().await;
867
868        // Enqueue a transaction with a new gas object, empty input.
869        let gas_object_new = Object::with_id_owner_version_for_testing(
870            ObjectID::random(),
871            0.into(),
872            Owner::AddressOwner(owner),
873        );
874        let transaction = make_transaction(gas_object_new.clone(), vec![]);
875        let tx_start_time = Instant::now();
876        execution_scheduler.enqueue_transactions(
877            vec![(transaction.clone(), ExecutionEnv::new())],
878            &state.epoch_store_for_testing(),
879        );
880        // scheduler should output no transaction yet.
881        sleep(Duration::from_secs(1)).await;
882        assert!(
883            rx_ready_certificates
884                .try_recv()
885                .is_err_and(|err| err == TryRecvError::Empty)
886        );
887
888        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
889
890        // Duplicated enqueue is allowed.
891        execution_scheduler.enqueue_transactions(
892            vec![(transaction.clone(), ExecutionEnv::new())],
893            &state.epoch_store_for_testing(),
894        );
895        sleep(Duration::from_secs(1)).await;
896        assert!(
897            rx_ready_certificates
898                .try_recv()
899                .is_err_and(|err| err == TryRecvError::Empty)
900        );
901
902        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
903
904        // Notify scheduler about availability of the gas object.
905        state
906            .get_cache_writer()
907            .write_object_entry_for_test(gas_object_new);
908        // scheduler should output the transaction eventually.
909        // We will see both the original and the duplicated transaction.
910        let pending_certificate = rx_ready_certificates.recv().await.unwrap();
911        let pending_certificate2 = rx_ready_certificates.recv().await.unwrap();
912        assert_eq!(
913            pending_certificate.certificate.digest(),
914            pending_certificate2.certificate.digest()
915        );
916
917        // Tests that pending certificate stats are recorded properly. The ready time should be
918        // 2 seconds apart from the enqueue time.
919        assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
920        assert!(
921            pending_certificate.stats.ready_time.unwrap() - pending_certificate.stats.enqueue_time
922                >= Duration::from_secs(2)
923        );
924
925        // Predent we have just executed the transaction.
926        drop(pending_certificate);
927        drop(pending_certificate2);
928
929        // scheduler should be empty at the end.
930        execution_scheduler.check_empty_for_testing().await;
931    }
932
933    // Tests when objects become available, correct set of transactions can be sent to execute.
934    // Specifically, we have following setup,
935    //         shared_object     shared_object_2
936    //       /    |    \     \    /
937    //    tx_0  tx_1  tx_2    tx_3
938    //     r      r     w      r
939    // And when shared_object is available, tx_0, tx_1, and tx_2 can be executed. And when
940    // shared_object_2 becomes available, tx_3 can be executed.
941    #[tokio::test(flavor = "current_thread", start_paused = true)]
942    async fn execution_scheduler_object_dependency() {
943        telemetry_subscribers::init_for_testing();
944        // Initialize an authority state, with gas objects and a shared object.
945        let (owner, _keypair) = deterministic_random_account_key();
946        let gas_objects: Vec<Object> = (0..10)
947            .map(|_| {
948                let gas_object_id = ObjectID::random();
949                Object::with_id_owner_for_testing(gas_object_id, owner)
950            })
951            .collect();
952        let shared_object = Object::shared_for_testing();
953        let initial_shared_version = shared_object.owner().start_version().unwrap();
954        let shared_object_2 = Object::shared_for_testing();
955        let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
956
957        let state = init_state_with_objects(
958            [
959                gas_objects.clone(),
960                vec![shared_object.clone(), shared_object_2.clone()],
961            ]
962            .concat(),
963        )
964        .await;
965
966        // Create a new execution scheduler instead of reusing the authority's, to examine
967        // execution_scheduler output from rx_ready_certificates.
968        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
969        // scheduler should output no transaction.
970        assert!(rx_ready_certificates.try_recv().is_err());
971
972        // Enqueue two transactions with the same shared object input in read-only mode.
973        let shared_version = 1000.into();
974        let shared_object_arg_read = ObjectArg::SharedObject {
975            id: shared_object.id(),
976            initial_shared_version,
977            mutability: SharedObjectMutability::Immutable,
978        };
979        let transaction_read_0 = make_transaction(
980            gas_objects[0].clone(),
981            vec![CallArg::Object(shared_object_arg_read)],
982        );
983        let transaction_read_1 = make_transaction(
984            gas_objects[1].clone(),
985            vec![CallArg::Object(shared_object_arg_read)],
986        );
987        let tx_read_0_assigned_versions = vec![(
988            (
989                shared_object.id(),
990                shared_object.owner().start_version().unwrap(),
991            ),
992            shared_version,
993        )];
994        let tx_read_1_assigned_versions = vec![(
995            (
996                shared_object.id(),
997                shared_object.owner().start_version().unwrap(),
998            ),
999            shared_version,
1000        )];
1001
1002        // Enqueue one transaction with the same shared object in mutable mode.
1003        let shared_object_arg_default = ObjectArg::SharedObject {
1004            id: shared_object.id(),
1005            initial_shared_version,
1006            mutability: SharedObjectMutability::Mutable,
1007        };
1008        let transaction_default = make_transaction(
1009            gas_objects[2].clone(),
1010            vec![CallArg::Object(shared_object_arg_default)],
1011        );
1012        let tx_default_assigned_versions = vec![(
1013            (
1014                shared_object.id(),
1015                shared_object.owner().start_version().unwrap(),
1016            ),
1017            shared_version,
1018        )];
1019
1020        // Enqueue one transaction with two readonly shared object inputs, `shared_object` and `shared_object_2`.
1021        let shared_version_2 = 1000.into();
1022        let shared_object_arg_read_2 = ObjectArg::SharedObject {
1023            id: shared_object_2.id(),
1024            initial_shared_version: initial_shared_version_2,
1025            mutability: SharedObjectMutability::Immutable,
1026        };
1027        let transaction_read_2 = make_transaction(
1028            gas_objects[3].clone(),
1029            vec![
1030                CallArg::Object(shared_object_arg_default),
1031                CallArg::Object(shared_object_arg_read_2),
1032            ],
1033        );
1034        let tx_read_2_assigned_versions = vec![
1035            (
1036                (
1037                    shared_object.id(),
1038                    shared_object.owner().start_version().unwrap(),
1039                ),
1040                shared_version,
1041            ),
1042            (
1043                (
1044                    shared_object_2.id(),
1045                    shared_object_2.owner().start_version().unwrap(),
1046                ),
1047                shared_version_2,
1048            ),
1049        ];
1050
1051        execution_scheduler.enqueue_transactions(
1052            vec![
1053                (
1054                    transaction_read_0.clone(),
1055                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1056                        tx_read_0_assigned_versions,
1057                        None,
1058                    )),
1059                ),
1060                (
1061                    transaction_read_1.clone(),
1062                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1063                        tx_read_1_assigned_versions,
1064                        None,
1065                    )),
1066                ),
1067                (
1068                    transaction_default.clone(),
1069                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1070                        tx_default_assigned_versions,
1071                        None,
1072                    )),
1073                ),
1074                (
1075                    transaction_read_2.clone(),
1076                    ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1077                        tx_read_2_assigned_versions,
1078                        None,
1079                    )),
1080                ),
1081            ],
1082            &state.epoch_store_for_testing(),
1083        );
1084
1085        // scheduler should output no transaction yet.
1086        sleep(Duration::from_secs(1)).await;
1087        assert!(rx_ready_certificates.try_recv().is_err());
1088
1089        assert_eq!(execution_scheduler.num_pending_certificates(), 4);
1090
1091        // Notify scheduler about availability of the first shared object.
1092        let mut new_shared_object = shared_object.clone();
1093        new_shared_object
1094            .data
1095            .try_as_move_mut()
1096            .unwrap()
1097            .increment_version_to(shared_version_2);
1098        state
1099            .get_cache_writer()
1100            .write_object_entry_for_test(new_shared_object);
1101
1102        // scheduler should output the 3 transactions that are only waiting for this object.
1103        let tx_0 = rx_ready_certificates.recv().await.unwrap().certificate;
1104        let tx_1 = rx_ready_certificates.recv().await.unwrap().certificate;
1105        let tx_2 = rx_ready_certificates.recv().await.unwrap().certificate;
1106        {
1107            let mut want_digests = vec![
1108                transaction_read_0.digest(),
1109                transaction_read_1.digest(),
1110                transaction_default.digest(),
1111            ];
1112            want_digests.sort();
1113            let mut got_digests = vec![tx_0.digest(), tx_1.digest(), tx_2.digest()];
1114            got_digests.sort();
1115            assert_eq!(want_digests, got_digests);
1116        }
1117
1118        sleep(Duration::from_secs(1)).await;
1119        assert!(rx_ready_certificates.try_recv().is_err());
1120
1121        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1122
1123        // Make shared_object_2 available.
1124        let mut new_shared_object_2 = shared_object_2.clone();
1125        new_shared_object_2
1126            .data
1127            .try_as_move_mut()
1128            .unwrap()
1129            .increment_version_to(shared_version_2);
1130        state
1131            .get_cache_writer()
1132            .write_object_entry_for_test(new_shared_object_2);
1133
1134        // Now, the transaction waiting for both shared objects can be executed.
1135        let tx_3 = rx_ready_certificates.recv().await.unwrap().certificate;
1136        assert_eq!(transaction_read_2.digest(), tx_3.digest());
1137
1138        sleep(Duration::from_secs(1)).await;
1139        assert!(rx_ready_certificates.try_recv().is_err());
1140
1141        execution_scheduler.check_empty_for_testing().await;
1142    }
1143
1144    #[tokio::test(flavor = "current_thread", start_paused = true)]
1145    async fn execution_scheduler_receiving_notify_commit() {
1146        telemetry_subscribers::init_for_testing();
1147        // Initialize an authority state.
1148        let (owner, _keypair) = deterministic_random_account_key();
1149        let gas_objects: Vec<Object> = (0..10)
1150            .map(|_| {
1151                let gas_object_id = ObjectID::random();
1152                Object::with_id_owner_for_testing(gas_object_id, owner)
1153            })
1154            .collect();
1155        let state = init_state_with_objects(gas_objects.clone()).await;
1156
1157        // Create a new execution scheduler instead of reusing the authority's, to examine
1158        // execution_scheduler output from rx_ready_certificates.
1159        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1160        // scheduler should output no transaction.
1161        assert!(rx_ready_certificates.try_recv().is_err());
1162        // scheduler should be empty at the beginning.
1163        execution_scheduler.check_empty_for_testing().await;
1164
1165        let obj_id = ObjectID::random();
1166        let object_arguments: Vec<_> = (0..10)
1167            .map(|i| {
1168                let object = Object::with_id_owner_version_for_testing(
1169                    obj_id,
1170                    i.into(),
1171                    Owner::AddressOwner(owner),
1172                );
1173                // Every other transaction receives the object, and we create a run of multiple receives in
1174                // a row at the beginning to test that the scheduler doesn't get stuck in either configuration of:
1175                // ImmOrOwnedObject => Receiving,
1176                // Receiving => Receiving
1177                // Receiving => ImmOrOwnedObject
1178                // ImmOrOwnedObject => ImmOrOwnedObject is already tested as the default case on mainnet.
1179                let object_arg = if i % 2 == 0 || i == 3 {
1180                    ObjectArg::Receiving(object.compute_object_reference())
1181                } else {
1182                    ObjectArg::ImmOrOwnedObject(object.compute_object_reference())
1183                };
1184                let txn =
1185                    make_transaction(gas_objects[0].clone(), vec![CallArg::Object(object_arg)]);
1186                (object, txn)
1187            })
1188            .collect();
1189
1190        for (i, (_, txn)) in object_arguments.iter().enumerate() {
1191            // scheduler should output no transaction yet since waiting on receiving object or
1192            // ImmOrOwnedObject input.
1193            execution_scheduler.enqueue_transactions(
1194                vec![(txn.clone(), ExecutionEnv::new())],
1195                &state.epoch_store_for_testing(),
1196            );
1197            sleep(Duration::from_secs(1)).await;
1198            assert!(rx_ready_certificates.try_recv().is_err());
1199            assert_eq!(execution_scheduler.num_pending_certificates(), i + 1);
1200        }
1201
1202        // Now start to unravel the transactions by notifying that each subsequent
1203        // transaction has been processed.
1204        let len = object_arguments.len();
1205        for (i, (object, txn)) in object_arguments.into_iter().enumerate() {
1206            // Mark the object as available.
1207            // We should now eventually see the transaction as ready.
1208            state
1209                .get_cache_writer()
1210                .write_object_entry_for_test(object.clone());
1211
1212            // scheduler should output the transaction eventually now that the receiving object has become
1213            // available.
1214            rx_ready_certificates.recv().await.unwrap();
1215
1216            // Only one transaction at a time should become available though. So if we try to get
1217            // another one it should fail.
1218            sleep(Duration::from_secs(1)).await;
1219            assert!(rx_ready_certificates.try_recv().is_err());
1220
1221            // Notify the scheduler that the transaction has been processed.
1222            drop(txn);
1223
1224            // scheduler should now output another transaction to run since it the next version of that object
1225            // has become available.
1226            assert_eq!(execution_scheduler.num_pending_certificates(), len - i - 1);
1227        }
1228
1229        // After everything scheduler should be empty.
1230        execution_scheduler.check_empty_for_testing().await;
1231    }
1232
1233    #[tokio::test(flavor = "current_thread", start_paused = true)]
1234    async fn execution_scheduler_receiving_object_ready_notifications() {
1235        telemetry_subscribers::init_for_testing();
1236        // Initialize an authority state.
1237        let (owner, _keypair) = deterministic_random_account_key();
1238        let gas_objects: Vec<Object> = (0..10)
1239            .map(|_| {
1240                let gas_object_id = ObjectID::random();
1241                Object::with_id_owner_for_testing(gas_object_id, owner)
1242            })
1243            .collect();
1244        let state = init_state_with_objects(gas_objects.clone()).await;
1245
1246        // Create a new execution scheduler instead of reusing the authority's, to examine
1247        // execution_scheduler output from rx_ready_certificates.
1248        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1249        // scheduler should output no transaction.
1250        assert!(rx_ready_certificates.try_recv().is_err());
1251        // scheduler should be empty at the beginning.
1252        execution_scheduler.check_empty_for_testing().await;
1253
1254        let obj_id = ObjectID::random();
1255        let receiving_object_new0 =
1256            Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1257        let receiving_object_new1 =
1258            Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1259        let receiving_object_arg0 =
1260            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1261        let receive_object_transaction0 = make_transaction(
1262            gas_objects[0].clone(),
1263            vec![CallArg::Object(receiving_object_arg0)],
1264        );
1265
1266        let receiving_object_arg1 =
1267            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1268        let receive_object_transaction1 = make_transaction(
1269            gas_objects[0].clone(),
1270            vec![CallArg::Object(receiving_object_arg1)],
1271        );
1272
1273        // scheduler should output no transaction yet since waiting on receiving object.
1274        execution_scheduler.enqueue_transactions(
1275            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1276            &state.epoch_store_for_testing(),
1277        );
1278        sleep(Duration::from_secs(1)).await;
1279        assert!(rx_ready_certificates.try_recv().is_err());
1280        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1281
1282        // scheduler should output no transaction yet since waiting on receiving object.
1283        execution_scheduler.enqueue_transactions(
1284            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1285            &state.epoch_store_for_testing(),
1286        );
1287        sleep(Duration::from_secs(1)).await;
1288        assert!(rx_ready_certificates.try_recv().is_err());
1289        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1290
1291        // Duplicate enqueue of receiving object is allowed.
1292        execution_scheduler.enqueue_transactions(
1293            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1294            &state.epoch_store_for_testing(),
1295        );
1296        sleep(Duration::from_secs(1)).await;
1297        assert!(rx_ready_certificates.try_recv().is_err());
1298        assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1299
1300        // Notify scheduler that the receiving object 0 is available.
1301        state
1302            .get_cache_writer()
1303            .write_object_entry_for_test(receiving_object_new0.clone());
1304
1305        // scheduler should output the transaction eventually now that the receiving object has become
1306        // available.
1307        rx_ready_certificates.recv().await.unwrap();
1308
1309        // Notify scheduler that the receiving object 0 is available.
1310        state
1311            .get_cache_writer()
1312            .write_object_entry_for_test(receiving_object_new1.clone());
1313
1314        // scheduler should output the transaction eventually now that the receiving object has become
1315        // available.
1316        rx_ready_certificates.recv().await.unwrap();
1317    }
1318
1319    #[tokio::test(flavor = "current_thread", start_paused = true)]
1320    async fn execution_scheduler_receiving_object_ready_notifications_multiple_of_same_receiving() {
1321        telemetry_subscribers::init_for_testing();
1322        // Initialize an authority state.
1323        let (owner, _keypair) = deterministic_random_account_key();
1324        let gas_objects: Vec<Object> = (0..10)
1325            .map(|_| {
1326                let gas_object_id = ObjectID::random();
1327                Object::with_id_owner_for_testing(gas_object_id, owner)
1328            })
1329            .collect();
1330        let state = init_state_with_objects(gas_objects.clone()).await;
1331
1332        // Create a new execution scheduler instead of reusing the authority's, to examine
1333        // execution_scheduler output from rx_ready_certificates.
1334        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1335        // scheduler should output no transaction.
1336        assert!(rx_ready_certificates.try_recv().is_err());
1337        // scheduler should be empty at the beginning.
1338        execution_scheduler.check_empty_for_testing().await;
1339
1340        let obj_id = ObjectID::random();
1341        let receiving_object_new0 =
1342            Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1343        let receiving_object_new1 =
1344            Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1345        let receiving_object_arg0 =
1346            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1347        let receive_object_transaction0 = make_transaction(
1348            gas_objects[0].clone(),
1349            vec![CallArg::Object(receiving_object_arg0)],
1350        );
1351
1352        let receive_object_transaction01 = make_transaction(
1353            gas_objects[1].clone(),
1354            vec![CallArg::Object(receiving_object_arg0)],
1355        );
1356
1357        let receiving_object_arg1 =
1358            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1359        let receive_object_transaction1 = make_transaction(
1360            gas_objects[0].clone(),
1361            vec![CallArg::Object(receiving_object_arg1)],
1362        );
1363
1364        // Enqueuing a transaction with a receiving object that is available at the time it is enqueued
1365        // should become immediately available.
1366        let gas_receiving_arg = ObjectArg::Receiving(gas_objects[3].compute_object_reference());
1367        let tx1 = make_transaction(
1368            gas_objects[0].clone(),
1369            vec![CallArg::Object(gas_receiving_arg)],
1370        );
1371
1372        // scheduler should output no transaction yet since waiting on receiving object.
1373        execution_scheduler.enqueue_transactions(
1374            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1375            &state.epoch_store_for_testing(),
1376        );
1377        sleep(Duration::from_secs(1)).await;
1378        assert!(rx_ready_certificates.try_recv().is_err());
1379        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1380
1381        // scheduler should output no transaction yet since waiting on receiving object.
1382        execution_scheduler.enqueue_transactions(
1383            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1384            &state.epoch_store_for_testing(),
1385        );
1386        sleep(Duration::from_secs(1)).await;
1387        assert!(rx_ready_certificates.try_recv().is_err());
1388        assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1389
1390        // Different transaction with a duplicate receiving object reference is allowed.
1391        // Both transaction's will be outputted once the receiving object is available.
1392        execution_scheduler.enqueue_transactions(
1393            vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1394            &state.epoch_store_for_testing(),
1395        );
1396        sleep(Duration::from_secs(1)).await;
1397        assert!(rx_ready_certificates.try_recv().is_err());
1398        assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1399
1400        // Notify scheduler that the receiving object 0 is available.
1401        state
1402            .get_cache_writer()
1403            .write_object_entry_for_test(receiving_object_new0.clone());
1404
1405        // scheduler should output both transactions depending on the receiving object now that the
1406        // transaction's receiving object has become available.
1407        rx_ready_certificates.recv().await.unwrap();
1408
1409        rx_ready_certificates.recv().await.unwrap();
1410
1411        // Only two transactions that were dependent on the receiving object should be output.
1412        assert!(rx_ready_certificates.try_recv().is_err());
1413
1414        // Enqueue a transaction with a receiving object that is available at the time it is enqueued.
1415        // This should be immediately available.
1416        execution_scheduler.enqueue_transactions(
1417            vec![(tx1.clone(), ExecutionEnv::new())],
1418            &state.epoch_store_for_testing(),
1419        );
1420        sleep(Duration::from_secs(1)).await;
1421        rx_ready_certificates.recv().await.unwrap();
1422
1423        // Notify scheduler that the receiving object 0 is available.
1424        state
1425            .get_cache_writer()
1426            .write_object_entry_for_test(receiving_object_new1.clone());
1427
1428        // scheduler should output the transaction eventually now that the receiving object has become
1429        // available.
1430        rx_ready_certificates.recv().await.unwrap();
1431    }
1432
1433    #[tokio::test(flavor = "current_thread", start_paused = true)]
1434    async fn execution_scheduler_receiving_object_ready_if_current_version_greater() {
1435        telemetry_subscribers::init_for_testing();
1436        // Initialize an authority state.
1437        let (owner, _keypair) = deterministic_random_account_key();
1438        let mut gas_objects: Vec<Object> = (0..10)
1439            .map(|_| {
1440                let gas_object_id = ObjectID::random();
1441                Object::with_id_owner_for_testing(gas_object_id, owner)
1442            })
1443            .collect();
1444        let receiving_object = Object::with_id_owner_version_for_testing(
1445            ObjectID::random(),
1446            10.into(),
1447            Owner::AddressOwner(owner),
1448        );
1449        gas_objects.push(receiving_object.clone());
1450        let state = init_state_with_objects(gas_objects.clone()).await;
1451
1452        // Create a new execution scheduler instead of reusing the authority's, to examine
1453        // execution_scheduler output from rx_ready_certificates.
1454        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1455        // scheduler should output no transaction.
1456        assert!(rx_ready_certificates.try_recv().is_err());
1457        // scheduler should be empty at the beginning.
1458        execution_scheduler.check_empty_for_testing().await;
1459
1460        let receiving_object_new0 = Object::with_id_owner_version_for_testing(
1461            receiving_object.id(),
1462            0.into(),
1463            Owner::AddressOwner(owner),
1464        );
1465        let receiving_object_new1 = Object::with_id_owner_version_for_testing(
1466            receiving_object.id(),
1467            1.into(),
1468            Owner::AddressOwner(owner),
1469        );
1470        let receiving_object_arg0 =
1471            ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1472        let receive_object_transaction0 = make_transaction(
1473            gas_objects[0].clone(),
1474            vec![CallArg::Object(receiving_object_arg0)],
1475        );
1476
1477        let receive_object_transaction01 = make_transaction(
1478            gas_objects[1].clone(),
1479            vec![CallArg::Object(receiving_object_arg0)],
1480        );
1481
1482        let receiving_object_arg1 =
1483            ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1484        let receive_object_transaction1 = make_transaction(
1485            gas_objects[0].clone(),
1486            vec![CallArg::Object(receiving_object_arg1)],
1487        );
1488
1489        // scheduler should output no transaction yet since waiting on receiving object.
1490        execution_scheduler.enqueue_transactions(
1491            vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1492            &state.epoch_store_for_testing(),
1493        );
1494        execution_scheduler.enqueue_transactions(
1495            vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1496            &state.epoch_store_for_testing(),
1497        );
1498        execution_scheduler.enqueue_transactions(
1499            vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1500            &state.epoch_store_for_testing(),
1501        );
1502        sleep(Duration::from_secs(1)).await;
1503        rx_ready_certificates.recv().await.unwrap();
1504        rx_ready_certificates.recv().await.unwrap();
1505        rx_ready_certificates.recv().await.unwrap();
1506        assert!(rx_ready_certificates.try_recv().is_err());
1507    }
1508
1509    // Tests transaction cancellation logic in execution scheduler. Mainly tests that for cancelled transaction,
1510    // execution scheduler only waits for all non-shared objects to be available before outputting the transaction.
1511    #[tokio::test(flavor = "current_thread", start_paused = true)]
1512    async fn execution_scheduler_with_cancelled_transactions() {
1513        // Initialize an authority state, with gas objects and 3 shared objects.
1514        let (owner, _keypair) = deterministic_random_account_key();
1515        let gas_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1516        let shared_object_1 = Object::shared_for_testing();
1517        let initial_shared_version_1 = shared_object_1.owner().start_version().unwrap();
1518        let shared_object_2 = Object::shared_for_testing();
1519        let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
1520        let owned_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1521
1522        let state = init_state_with_objects(vec![
1523            gas_object.clone(),
1524            shared_object_1.clone(),
1525            shared_object_2.clone(),
1526            owned_object.clone(),
1527        ])
1528        .await;
1529
1530        // Create a new execution scheduler instead of reusing the authority's, to examine
1531        // execution_scheduler output from rx_ready_certificates.
1532        let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1533        // scheduler should output no transaction.
1534        assert!(rx_ready_certificates.try_recv().is_err());
1535
1536        // Enqueue one transaction with 2 shared object inputs and 1 owned input.
1537        let shared_object_arg_1 = ObjectArg::SharedObject {
1538            id: shared_object_1.id(),
1539            initial_shared_version: initial_shared_version_1,
1540            mutability: SharedObjectMutability::Mutable,
1541        };
1542        let shared_object_arg_2 = ObjectArg::SharedObject {
1543            id: shared_object_2.id(),
1544            initial_shared_version: initial_shared_version_2,
1545            mutability: SharedObjectMutability::Mutable,
1546        };
1547
1548        // Changes the desired owned object version to a higher version. We will make it available later.
1549        let owned_version = 2000.into();
1550        let mut owned_ref = owned_object.compute_object_reference();
1551        owned_ref.1 = owned_version;
1552        let owned_object_arg = ObjectArg::ImmOrOwnedObject(owned_ref);
1553
1554        let cancelled_transaction = make_transaction(
1555            gas_object.clone(),
1556            vec![
1557                CallArg::Object(shared_object_arg_1),
1558                CallArg::Object(shared_object_arg_2),
1559                CallArg::Object(owned_object_arg),
1560            ],
1561        );
1562        let assigned_versions = vec![
1563            (
1564                (
1565                    shared_object_1.id(),
1566                    shared_object_1.owner().start_version().unwrap(),
1567                ),
1568                SequenceNumber::CANCELLED_READ,
1569            ),
1570            (
1571                (
1572                    shared_object_2.id(),
1573                    shared_object_2.owner().start_version().unwrap(),
1574                ),
1575                SequenceNumber::CONGESTED,
1576            ),
1577        ];
1578
1579        execution_scheduler.enqueue_transactions(
1580            vec![(
1581                cancelled_transaction.clone(),
1582                ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1583                    assigned_versions,
1584                    None,
1585                )),
1586            )],
1587            &state.epoch_store_for_testing(),
1588        );
1589
1590        // scheduler should output no transaction yet.
1591        sleep(Duration::from_secs(1)).await;
1592        assert!(rx_ready_certificates.try_recv().is_err());
1593
1594        assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1595
1596        // Notify scheduler about availability of the owned object.
1597        let mut new_owned_object = owned_object.clone();
1598        new_owned_object
1599            .data
1600            .try_as_move_mut()
1601            .unwrap()
1602            .increment_version_to(owned_version);
1603        state
1604            .get_cache_writer()
1605            .write_object_entry_for_test(new_owned_object);
1606
1607        // scheduler should output the transaction as soon as the owned object is available.
1608        let available_txn = rx_ready_certificates.recv().await.unwrap().certificate;
1609        assert_eq!(available_txn.digest(), cancelled_transaction.digest());
1610
1611        sleep(Duration::from_secs(1)).await;
1612        assert!(rx_ready_certificates.try_recv().is_err());
1613
1614        execution_scheduler.check_empty_for_testing().await;
1615    }
1616
1617    #[test]
1618    fn test_barrier_dependency_builder() {
1619        let make_transaction = |non_exclusive_writes: Vec<u32>, exclusive_writes: Vec<u32>| {
1620            assert!(
1621                non_exclusive_writes
1622                    .iter()
1623                    .all(|id| !exclusive_writes.contains(id))
1624            );
1625            assert!(
1626                exclusive_writes
1627                    .iter()
1628                    .all(|id| !non_exclusive_writes.contains(id))
1629            );
1630
1631            let non_exclusive_writes = non_exclusive_writes
1632                .into_iter()
1633                .map(|id| ObjectID::from_single_byte(id as u8));
1634            let exclusive_writes = exclusive_writes
1635                .into_iter()
1636                .map(|id| ObjectID::from_single_byte(id as u8));
1637            let mut builder = ProgrammableTransactionBuilder::new();
1638            for non_exclusive_write in non_exclusive_writes {
1639                builder
1640                    .obj(ObjectArg::SharedObject {
1641                        id: non_exclusive_write,
1642                        initial_shared_version: SequenceNumber::new(),
1643                        mutability: SharedObjectMutability::NonExclusiveWrite,
1644                    })
1645                    .unwrap();
1646            }
1647
1648            for exclusive_write in exclusive_writes {
1649                builder
1650                    .obj(ObjectArg::SharedObject {
1651                        id: exclusive_write,
1652                        initial_shared_version: SequenceNumber::new(),
1653                        mutability: SharedObjectMutability::Mutable,
1654                    })
1655                    .unwrap();
1656            }
1657
1658            let tx = TransactionKind::ProgrammableTransaction(builder.finish());
1659            let tx_data =
1660                TransactionData::new(tx, SuiAddress::default(), random_object_ref(), 1, 1);
1661            Transaction::from_data_and_signer(tx_data, vec![])
1662        };
1663
1664        // One non-exclusive write, one exclusive write.
1665        {
1666            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1667            let tx1 = make_transaction(vec![1], vec![]);
1668            let tx2 = make_transaction(vec![], vec![1]);
1669
1670            let tx1_deps =
1671                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1672            let tx2_deps =
1673                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1674            assert!(tx1_deps.is_empty());
1675            assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1676        }
1677
1678        // One transaction has non-exclusive writes to two different objects, and then becomes
1679        // a dependency of two barriers
1680        {
1681            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1682            let tx1 = make_transaction(vec![1, 2], vec![]);
1683            let tx2 = make_transaction(vec![], vec![1]);
1684            let tx3 = make_transaction(vec![], vec![2]);
1685
1686            let tx1_deps =
1687                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1688            let tx2_deps =
1689                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1690            let tx3_deps =
1691                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1692            assert!(tx1_deps.is_empty());
1693            assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1694            assert_eq!(Vec::from_iter(tx3_deps), vec![*tx1.digest()]);
1695        }
1696
1697        // Ensure multiple-object dependences are merged
1698        {
1699            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1700            let tx1 = make_transaction(vec![1], vec![]);
1701            let tx2 = make_transaction(vec![2], vec![]);
1702            let tx3 = make_transaction(vec![], vec![1, 2]);
1703
1704            let tx1_deps =
1705                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1706            let tx2_deps =
1707                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1708            let tx3_deps =
1709                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1710            assert!(tx1_deps.is_empty());
1711            assert!(tx2_deps.is_empty());
1712            assert_eq!(tx3_deps, BTreeSet::from([*tx1.digest(), *tx2.digest()]));
1713        }
1714
1715        // Ensure dependency state is cleared
1716        {
1717            let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1718            let tx1 = make_transaction(vec![1], vec![]);
1719            let tx2 = make_transaction(vec![], vec![1]);
1720            let tx3 = make_transaction(vec![], vec![1]);
1721
1722            let tx1_deps =
1723                barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1724            let tx2_deps =
1725                barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1726            let tx3_deps =
1727                barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1728            assert!(tx1_deps.is_empty());
1729            assert_eq!(tx2_deps, BTreeSet::from([*tx1.digest()]));
1730            assert!(tx3_deps.is_empty());
1731        }
1732    }
1733}