1use 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
48pub(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 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 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 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 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 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 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 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 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 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 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 }
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 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 #[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 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 let reservations = Self::collect_withdraw_reservations(&certs, epoch_store);
547 let certs = self.causal_admission.dedup_and_assign(certs);
548
549 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 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 pub fn enqueue_transactions(
588 &self,
589 certs: Vec<(VerifiedExecutableTransaction, ExecutionEnv)>,
590 epoch_store: &Arc<AuthorityPerEpochStore>,
591 ) {
592 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 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 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 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 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)] fn make_execution_scheduler(
776 state: &AuthorityState,
777 ) -> (ExecutionScheduler, UnboundedReceiver<PendingCertificate>) {
778 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 ®istry,
791 );
792
793 (execution_scheduler, rx_ready_certificates)
794 }
795
796 fn make_transaction(gas_object: Object, input: Vec<CallArg>) -> VerifiedExecutableTransaction {
797 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 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
826 assert!(
828 rx_ready_certificates
829 .try_recv()
830 .is_err_and(|err| err == TryRecvError::Empty)
831 );
832 assert_eq!(execution_scheduler.num_pending_certificates(), 0);
834
835 execution_scheduler.enqueue_transactions(vec![], &state.epoch_store_for_testing());
837 assert!(
839 rx_ready_certificates
840 .try_recv()
841 .is_err_and(|err| err == TryRecvError::Empty)
842 );
843
844 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 let pending_certificate = rx_ready_certificates.recv().await.unwrap();
853
854 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 drop(pending_certificate);
864
865 execution_scheduler.check_empty_for_testing().await;
867
868 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 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 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 state
906 .get_cache_writer()
907 .write_object_entry_for_test(gas_object_new);
908 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 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 drop(pending_certificate);
927 drop(pending_certificate2);
928
929 execution_scheduler.check_empty_for_testing().await;
931 }
932
933 #[tokio::test(flavor = "current_thread", start_paused = true)]
942 async fn execution_scheduler_object_dependency() {
943 telemetry_subscribers::init_for_testing();
944 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
969 assert!(rx_ready_certificates.try_recv().is_err());
971
972 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 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 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 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 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 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 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 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 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1160 assert!(rx_ready_certificates.try_recv().is_err());
1162 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 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 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 let len = object_arguments.len();
1205 for (i, (object, txn)) in object_arguments.into_iter().enumerate() {
1206 state
1209 .get_cache_writer()
1210 .write_object_entry_for_test(object.clone());
1211
1212 rx_ready_certificates.recv().await.unwrap();
1215
1216 sleep(Duration::from_secs(1)).await;
1219 assert!(rx_ready_certificates.try_recv().is_err());
1220
1221 drop(txn);
1223
1224 assert_eq!(execution_scheduler.num_pending_certificates(), len - i - 1);
1227 }
1228
1229 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 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1249 assert!(rx_ready_certificates.try_recv().is_err());
1251 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 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 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 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 state
1302 .get_cache_writer()
1303 .write_object_entry_for_test(receiving_object_new0.clone());
1304
1305 rx_ready_certificates.recv().await.unwrap();
1308
1309 state
1311 .get_cache_writer()
1312 .write_object_entry_for_test(receiving_object_new1.clone());
1313
1314 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 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1335 assert!(rx_ready_certificates.try_recv().is_err());
1337 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 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 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 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 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 state
1402 .get_cache_writer()
1403 .write_object_entry_for_test(receiving_object_new0.clone());
1404
1405 rx_ready_certificates.recv().await.unwrap();
1408
1409 rx_ready_certificates.recv().await.unwrap();
1410
1411 assert!(rx_ready_certificates.try_recv().is_err());
1413
1414 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 state
1425 .get_cache_writer()
1426 .write_object_entry_for_test(receiving_object_new1.clone());
1427
1428 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 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1455 assert!(rx_ready_certificates.try_recv().is_err());
1457 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 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 #[tokio::test(flavor = "current_thread", start_paused = true)]
1512 async fn execution_scheduler_with_cancelled_transactions() {
1513 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 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1533 assert!(rx_ready_certificates.try_recv().is_err());
1535
1536 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 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 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 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 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 {
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 {
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 {
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 {
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}