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_macros::sim_test;
754 use sui_test_transaction_builder::TestTransactionBuilder;
755 use sui_types::base_types::{SuiAddress, random_object_ref};
756 use sui_types::executable_transaction::VerifiedExecutableTransaction;
757 use sui_types::object::Owner;
758 use sui_types::programmable_transaction_builder::ProgrammableTransactionBuilder;
759 use sui_types::transaction::{
760 SharedObjectMutability, Transaction, TransactionData, TransactionKind, VerifiedTransaction,
761 };
762 use sui_types::{
763 SUI_FRAMEWORK_PACKAGE_ID,
764 base_types::{ObjectID, SequenceNumber},
765 crypto::deterministic_random_account_key,
766 object::Object,
767 transaction::{CallArg, ObjectArg},
768 };
769 use tokio::time::Instant;
770 use tokio::{
771 sync::mpsc::{UnboundedReceiver, error::TryRecvError, unbounded_channel},
772 time::sleep,
773 };
774
775 #[allow(clippy::disallowed_methods)] fn make_execution_scheduler(
777 state: &AuthorityState,
778 ) -> (ExecutionScheduler, UnboundedReceiver<PendingCertificate>) {
779 let (tx_ready_certificates, rx_ready_certificates) = unbounded_channel();
782 let registry = prometheus::Registry::new();
783 let execution_scheduler = ExecutionScheduler::new(
784 state.get_object_cache_reader().clone(),
785 state.get_account_funds_read().clone(),
786 state.get_transaction_cache_reader().clone(),
787 tx_ready_certificates,
788 &state.epoch_store_for_testing(),
789 FundsWithdrawSchedulerType::default(),
790 state.metrics.clone(),
791 ®istry,
792 );
793
794 (execution_scheduler, rx_ready_certificates)
795 }
796
797 fn make_transaction(gas_object: Object, input: Vec<CallArg>) -> VerifiedExecutableTransaction {
798 let rgp = 100;
801 let (sender, keypair) = deterministic_random_account_key();
802 let transaction =
803 TestTransactionBuilder::new(sender, gas_object.compute_object_reference(), rgp)
804 .move_call(SUI_FRAMEWORK_PACKAGE_ID, "counter", "assert_value", input)
805 .build_and_sign(&keypair);
806 VerifiedExecutableTransaction::new_system(
807 VerifiedTransaction::new_unchecked(transaction),
808 0,
809 )
810 }
811
812 #[tokio::test(flavor = "current_thread", start_paused = true)]
813 async fn execution_scheduler_basics() {
814 let (owner, _keypair) = deterministic_random_account_key();
816 let gas_objects: Vec<Object> = (0..10)
817 .map(|_| {
818 let gas_object_id = ObjectID::random();
819 Object::with_id_owner_for_testing(gas_object_id, owner)
820 })
821 .collect();
822 let state = init_state_with_objects(gas_objects.clone()).await;
823
824 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
827 assert!(
829 rx_ready_certificates
830 .try_recv()
831 .is_err_and(|err| err == TryRecvError::Empty)
832 );
833 assert_eq!(execution_scheduler.num_pending_certificates(), 0);
835
836 execution_scheduler.enqueue_transactions(vec![], &state.epoch_store_for_testing());
838 assert!(
840 rx_ready_certificates
841 .try_recv()
842 .is_err_and(|err| err == TryRecvError::Empty)
843 );
844
845 let transaction = make_transaction(gas_objects[0].clone(), vec![]);
847 let tx_start_time = Instant::now();
848 execution_scheduler.enqueue_transactions(
849 vec![(transaction.clone(), ExecutionEnv::new())],
850 &state.epoch_store_for_testing(),
851 );
852 let pending_certificate = rx_ready_certificates.recv().await.unwrap();
854
855 assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
857 assert!(
858 pending_certificate.stats.ready_time.unwrap() >= pending_certificate.stats.enqueue_time
859 );
860
861 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
862
863 drop(pending_certificate);
865
866 execution_scheduler.check_empty_for_testing().await;
868
869 let gas_object_new = Object::with_id_owner_version_for_testing(
871 ObjectID::random(),
872 0.into(),
873 Owner::AddressOwner(owner),
874 );
875 let transaction = make_transaction(gas_object_new.clone(), vec![]);
876 let tx_start_time = Instant::now();
877 execution_scheduler.enqueue_transactions(
878 vec![(transaction.clone(), ExecutionEnv::new())],
879 &state.epoch_store_for_testing(),
880 );
881 sleep(Duration::from_secs(1)).await;
883 assert!(
884 rx_ready_certificates
885 .try_recv()
886 .is_err_and(|err| err == TryRecvError::Empty)
887 );
888
889 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
890
891 execution_scheduler.enqueue_transactions(
893 vec![(transaction.clone(), ExecutionEnv::new())],
894 &state.epoch_store_for_testing(),
895 );
896 sleep(Duration::from_secs(1)).await;
897 assert!(
898 rx_ready_certificates
899 .try_recv()
900 .is_err_and(|err| err == TryRecvError::Empty)
901 );
902
903 assert_eq!(execution_scheduler.num_pending_certificates(), 2);
904
905 state
907 .get_cache_writer()
908 .write_object_entry_for_test(gas_object_new);
909 let pending_certificate = rx_ready_certificates.recv().await.unwrap();
912 let pending_certificate2 = rx_ready_certificates.recv().await.unwrap();
913 assert_eq!(
914 pending_certificate.certificate.digest(),
915 pending_certificate2.certificate.digest()
916 );
917
918 assert!(pending_certificate.stats.enqueue_time >= tx_start_time);
921 assert!(
922 pending_certificate.stats.ready_time.unwrap() - pending_certificate.stats.enqueue_time
923 >= Duration::from_secs(2)
924 );
925
926 drop(pending_certificate);
928 drop(pending_certificate2);
929
930 execution_scheduler.check_empty_for_testing().await;
932 }
933
934 #[sim_test]
943 async fn execution_scheduler_object_dependency() {
944 telemetry_subscribers::init_for_testing();
945 let (owner, _keypair) = deterministic_random_account_key();
947 let gas_objects: Vec<Object> = (0..10)
948 .map(|_| {
949 let gas_object_id = ObjectID::random();
950 Object::with_id_owner_for_testing(gas_object_id, owner)
951 })
952 .collect();
953 let shared_object = Object::shared_for_testing();
954 let initial_shared_version = shared_object.owner().start_version().unwrap();
955 let shared_object_2 = Object::shared_for_testing();
956 let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
957
958 let state = init_state_with_objects(
959 [
960 gas_objects.clone(),
961 vec![shared_object.clone(), shared_object_2.clone()],
962 ]
963 .concat(),
964 )
965 .await;
966
967 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
970 assert!(rx_ready_certificates.try_recv().is_err());
972
973 let shared_version = 1000.into();
975 let shared_object_arg_read = ObjectArg::SharedObject {
976 id: shared_object.id(),
977 initial_shared_version,
978 mutability: SharedObjectMutability::Immutable,
979 };
980 let transaction_read_0 = make_transaction(
981 gas_objects[0].clone(),
982 vec![CallArg::Object(shared_object_arg_read)],
983 );
984 let transaction_read_1 = make_transaction(
985 gas_objects[1].clone(),
986 vec![CallArg::Object(shared_object_arg_read)],
987 );
988 let tx_read_0_assigned_versions = vec![(
989 (
990 shared_object.id(),
991 shared_object.owner().start_version().unwrap(),
992 ),
993 shared_version,
994 )];
995 let tx_read_1_assigned_versions = vec![(
996 (
997 shared_object.id(),
998 shared_object.owner().start_version().unwrap(),
999 ),
1000 shared_version,
1001 )];
1002
1003 let shared_object_arg_default = ObjectArg::SharedObject {
1005 id: shared_object.id(),
1006 initial_shared_version,
1007 mutability: SharedObjectMutability::Mutable,
1008 };
1009 let transaction_default = make_transaction(
1010 gas_objects[2].clone(),
1011 vec![CallArg::Object(shared_object_arg_default)],
1012 );
1013 let tx_default_assigned_versions = vec![(
1014 (
1015 shared_object.id(),
1016 shared_object.owner().start_version().unwrap(),
1017 ),
1018 shared_version,
1019 )];
1020
1021 let shared_version_2 = 1000.into();
1023 let shared_object_arg_read_2 = ObjectArg::SharedObject {
1024 id: shared_object_2.id(),
1025 initial_shared_version: initial_shared_version_2,
1026 mutability: SharedObjectMutability::Immutable,
1027 };
1028 let transaction_read_2 = make_transaction(
1029 gas_objects[3].clone(),
1030 vec![
1031 CallArg::Object(shared_object_arg_default),
1032 CallArg::Object(shared_object_arg_read_2),
1033 ],
1034 );
1035 let tx_read_2_assigned_versions = vec![
1036 (
1037 (
1038 shared_object.id(),
1039 shared_object.owner().start_version().unwrap(),
1040 ),
1041 shared_version,
1042 ),
1043 (
1044 (
1045 shared_object_2.id(),
1046 shared_object_2.owner().start_version().unwrap(),
1047 ),
1048 shared_version_2,
1049 ),
1050 ];
1051
1052 execution_scheduler.enqueue_transactions(
1053 vec![
1054 (
1055 transaction_read_0.clone(),
1056 ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1057 tx_read_0_assigned_versions,
1058 None,
1059 )),
1060 ),
1061 (
1062 transaction_read_1.clone(),
1063 ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1064 tx_read_1_assigned_versions,
1065 None,
1066 )),
1067 ),
1068 (
1069 transaction_default.clone(),
1070 ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1071 tx_default_assigned_versions,
1072 None,
1073 )),
1074 ),
1075 (
1076 transaction_read_2.clone(),
1077 ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1078 tx_read_2_assigned_versions,
1079 None,
1080 )),
1081 ),
1082 ],
1083 &state.epoch_store_for_testing(),
1084 );
1085
1086 sleep(Duration::from_secs(1)).await;
1088 assert!(rx_ready_certificates.try_recv().is_err());
1089
1090 assert_eq!(execution_scheduler.num_pending_certificates(), 4);
1091
1092 let mut new_shared_object = shared_object.clone();
1094 new_shared_object
1095 .data
1096 .try_as_move_mut()
1097 .unwrap()
1098 .increment_version_to(shared_version_2);
1099 state
1100 .get_cache_writer()
1101 .write_object_entry_for_test(new_shared_object);
1102
1103 let tx_0 = rx_ready_certificates.recv().await.unwrap().certificate;
1105 let tx_1 = rx_ready_certificates.recv().await.unwrap().certificate;
1106 let tx_2 = rx_ready_certificates.recv().await.unwrap().certificate;
1107 {
1108 let mut want_digests = vec![
1109 transaction_read_0.digest(),
1110 transaction_read_1.digest(),
1111 transaction_default.digest(),
1112 ];
1113 want_digests.sort();
1114 let mut got_digests = vec![tx_0.digest(), tx_1.digest(), tx_2.digest()];
1115 got_digests.sort();
1116 assert_eq!(want_digests, got_digests);
1117 }
1118
1119 sleep(Duration::from_secs(1)).await;
1120 assert!(rx_ready_certificates.try_recv().is_err());
1121
1122 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1123
1124 let mut new_shared_object_2 = shared_object_2.clone();
1126 new_shared_object_2
1127 .data
1128 .try_as_move_mut()
1129 .unwrap()
1130 .increment_version_to(shared_version_2);
1131 state
1132 .get_cache_writer()
1133 .write_object_entry_for_test(new_shared_object_2);
1134
1135 let tx_3 = rx_ready_certificates.recv().await.unwrap().certificate;
1137 assert_eq!(transaction_read_2.digest(), tx_3.digest());
1138
1139 sleep(Duration::from_secs(1)).await;
1140 assert!(rx_ready_certificates.try_recv().is_err());
1141
1142 execution_scheduler.check_empty_for_testing().await;
1143 }
1144
1145 #[sim_test]
1146 async fn execution_scheduler_receiving_notify_commit() {
1147 telemetry_subscribers::init_for_testing();
1148 let (owner, _keypair) = deterministic_random_account_key();
1150 let gas_objects: Vec<Object> = (0..10)
1151 .map(|_| {
1152 let gas_object_id = ObjectID::random();
1153 Object::with_id_owner_for_testing(gas_object_id, owner)
1154 })
1155 .collect();
1156 let state = init_state_with_objects(gas_objects.clone()).await;
1157
1158 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1161 assert!(rx_ready_certificates.try_recv().is_err());
1163 execution_scheduler.check_empty_for_testing().await;
1165
1166 let obj_id = ObjectID::random();
1167 let object_arguments: Vec<_> = (0..10)
1168 .map(|i| {
1169 let object = Object::with_id_owner_version_for_testing(
1170 obj_id,
1171 i.into(),
1172 Owner::AddressOwner(owner),
1173 );
1174 let object_arg = if i % 2 == 0 || i == 3 {
1181 ObjectArg::Receiving(object.compute_object_reference())
1182 } else {
1183 ObjectArg::ImmOrOwnedObject(object.compute_object_reference())
1184 };
1185 let txn =
1186 make_transaction(gas_objects[0].clone(), vec![CallArg::Object(object_arg)]);
1187 (object, txn)
1188 })
1189 .collect();
1190
1191 for (i, (_, txn)) in object_arguments.iter().enumerate() {
1192 execution_scheduler.enqueue_transactions(
1195 vec![(txn.clone(), ExecutionEnv::new())],
1196 &state.epoch_store_for_testing(),
1197 );
1198 sleep(Duration::from_secs(1)).await;
1199 assert!(rx_ready_certificates.try_recv().is_err());
1200 assert_eq!(execution_scheduler.num_pending_certificates(), i + 1);
1201 }
1202
1203 let len = object_arguments.len();
1206 for (i, (object, txn)) in object_arguments.into_iter().enumerate() {
1207 state
1210 .get_cache_writer()
1211 .write_object_entry_for_test(object.clone());
1212
1213 rx_ready_certificates.recv().await.unwrap();
1216
1217 sleep(Duration::from_secs(1)).await;
1220 assert!(rx_ready_certificates.try_recv().is_err());
1221
1222 drop(txn);
1224
1225 assert_eq!(execution_scheduler.num_pending_certificates(), len - i - 1);
1228 }
1229
1230 execution_scheduler.check_empty_for_testing().await;
1232 }
1233
1234 #[sim_test]
1235 async fn execution_scheduler_receiving_object_ready_notifications() {
1236 telemetry_subscribers::init_for_testing();
1237 let (owner, _keypair) = deterministic_random_account_key();
1239 let gas_objects: Vec<Object> = (0..10)
1240 .map(|_| {
1241 let gas_object_id = ObjectID::random();
1242 Object::with_id_owner_for_testing(gas_object_id, owner)
1243 })
1244 .collect();
1245 let state = init_state_with_objects(gas_objects.clone()).await;
1246
1247 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1250 assert!(rx_ready_certificates.try_recv().is_err());
1252 execution_scheduler.check_empty_for_testing().await;
1254
1255 let obj_id = ObjectID::random();
1256 let receiving_object_new0 =
1257 Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1258 let receiving_object_new1 =
1259 Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1260 let receiving_object_arg0 =
1261 ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1262 let receive_object_transaction0 = make_transaction(
1263 gas_objects[0].clone(),
1264 vec![CallArg::Object(receiving_object_arg0)],
1265 );
1266
1267 let receiving_object_arg1 =
1268 ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1269 let receive_object_transaction1 = make_transaction(
1270 gas_objects[0].clone(),
1271 vec![CallArg::Object(receiving_object_arg1)],
1272 );
1273
1274 execution_scheduler.enqueue_transactions(
1276 vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1277 &state.epoch_store_for_testing(),
1278 );
1279 sleep(Duration::from_secs(1)).await;
1280 assert!(rx_ready_certificates.try_recv().is_err());
1281 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1282
1283 execution_scheduler.enqueue_transactions(
1285 vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1286 &state.epoch_store_for_testing(),
1287 );
1288 sleep(Duration::from_secs(1)).await;
1289 assert!(rx_ready_certificates.try_recv().is_err());
1290 assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1291
1292 execution_scheduler.enqueue_transactions(
1294 vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1295 &state.epoch_store_for_testing(),
1296 );
1297 sleep(Duration::from_secs(1)).await;
1298 assert!(rx_ready_certificates.try_recv().is_err());
1299 assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1300
1301 state
1303 .get_cache_writer()
1304 .write_object_entry_for_test(receiving_object_new0.clone());
1305
1306 rx_ready_certificates.recv().await.unwrap();
1309
1310 state
1312 .get_cache_writer()
1313 .write_object_entry_for_test(receiving_object_new1.clone());
1314
1315 rx_ready_certificates.recv().await.unwrap();
1318 }
1319
1320 #[sim_test]
1321 async fn execution_scheduler_receiving_object_ready_notifications_multiple_of_same_receiving() {
1322 telemetry_subscribers::init_for_testing();
1323 let (owner, _keypair) = deterministic_random_account_key();
1325 let gas_objects: Vec<Object> = (0..10)
1326 .map(|_| {
1327 let gas_object_id = ObjectID::random();
1328 Object::with_id_owner_for_testing(gas_object_id, owner)
1329 })
1330 .collect();
1331 let state = init_state_with_objects(gas_objects.clone()).await;
1332
1333 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1336 assert!(rx_ready_certificates.try_recv().is_err());
1338 execution_scheduler.check_empty_for_testing().await;
1340
1341 let obj_id = ObjectID::random();
1342 let receiving_object_new0 =
1343 Object::with_id_owner_version_for_testing(obj_id, 0.into(), Owner::AddressOwner(owner));
1344 let receiving_object_new1 =
1345 Object::with_id_owner_version_for_testing(obj_id, 1.into(), Owner::AddressOwner(owner));
1346 let receiving_object_arg0 =
1347 ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1348 let receive_object_transaction0 = make_transaction(
1349 gas_objects[0].clone(),
1350 vec![CallArg::Object(receiving_object_arg0)],
1351 );
1352
1353 let receive_object_transaction01 = make_transaction(
1354 gas_objects[1].clone(),
1355 vec![CallArg::Object(receiving_object_arg0)],
1356 );
1357
1358 let receiving_object_arg1 =
1359 ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1360 let receive_object_transaction1 = make_transaction(
1361 gas_objects[0].clone(),
1362 vec![CallArg::Object(receiving_object_arg1)],
1363 );
1364
1365 let gas_receiving_arg = ObjectArg::Receiving(gas_objects[3].compute_object_reference());
1368 let tx1 = make_transaction(
1369 gas_objects[0].clone(),
1370 vec![CallArg::Object(gas_receiving_arg)],
1371 );
1372
1373 execution_scheduler.enqueue_transactions(
1375 vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1376 &state.epoch_store_for_testing(),
1377 );
1378 sleep(Duration::from_secs(1)).await;
1379 assert!(rx_ready_certificates.try_recv().is_err());
1380 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1381
1382 execution_scheduler.enqueue_transactions(
1384 vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1385 &state.epoch_store_for_testing(),
1386 );
1387 sleep(Duration::from_secs(1)).await;
1388 assert!(rx_ready_certificates.try_recv().is_err());
1389 assert_eq!(execution_scheduler.num_pending_certificates(), 2);
1390
1391 execution_scheduler.enqueue_transactions(
1394 vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1395 &state.epoch_store_for_testing(),
1396 );
1397 sleep(Duration::from_secs(1)).await;
1398 assert!(rx_ready_certificates.try_recv().is_err());
1399 assert_eq!(execution_scheduler.num_pending_certificates(), 3);
1400
1401 state
1403 .get_cache_writer()
1404 .write_object_entry_for_test(receiving_object_new0.clone());
1405
1406 rx_ready_certificates.recv().await.unwrap();
1409
1410 rx_ready_certificates.recv().await.unwrap();
1411
1412 assert!(rx_ready_certificates.try_recv().is_err());
1414
1415 execution_scheduler.enqueue_transactions(
1418 vec![(tx1.clone(), ExecutionEnv::new())],
1419 &state.epoch_store_for_testing(),
1420 );
1421 sleep(Duration::from_secs(1)).await;
1422 rx_ready_certificates.recv().await.unwrap();
1423
1424 state
1426 .get_cache_writer()
1427 .write_object_entry_for_test(receiving_object_new1.clone());
1428
1429 rx_ready_certificates.recv().await.unwrap();
1432 }
1433
1434 #[sim_test]
1435 async fn execution_scheduler_receiving_object_ready_if_current_version_greater() {
1436 telemetry_subscribers::init_for_testing();
1437 let (owner, _keypair) = deterministic_random_account_key();
1439 let mut gas_objects: Vec<Object> = (0..10)
1440 .map(|_| {
1441 let gas_object_id = ObjectID::random();
1442 Object::with_id_owner_for_testing(gas_object_id, owner)
1443 })
1444 .collect();
1445 let receiving_object = Object::with_id_owner_version_for_testing(
1446 ObjectID::random(),
1447 10.into(),
1448 Owner::AddressOwner(owner),
1449 );
1450 gas_objects.push(receiving_object.clone());
1451 let state = init_state_with_objects(gas_objects.clone()).await;
1452
1453 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1456 assert!(rx_ready_certificates.try_recv().is_err());
1458 execution_scheduler.check_empty_for_testing().await;
1460
1461 let receiving_object_new0 = Object::with_id_owner_version_for_testing(
1462 receiving_object.id(),
1463 0.into(),
1464 Owner::AddressOwner(owner),
1465 );
1466 let receiving_object_new1 = Object::with_id_owner_version_for_testing(
1467 receiving_object.id(),
1468 1.into(),
1469 Owner::AddressOwner(owner),
1470 );
1471 let receiving_object_arg0 =
1472 ObjectArg::Receiving(receiving_object_new0.compute_object_reference());
1473 let receive_object_transaction0 = make_transaction(
1474 gas_objects[0].clone(),
1475 vec![CallArg::Object(receiving_object_arg0)],
1476 );
1477
1478 let receive_object_transaction01 = make_transaction(
1479 gas_objects[1].clone(),
1480 vec![CallArg::Object(receiving_object_arg0)],
1481 );
1482
1483 let receiving_object_arg1 =
1484 ObjectArg::Receiving(receiving_object_new1.compute_object_reference());
1485 let receive_object_transaction1 = make_transaction(
1486 gas_objects[0].clone(),
1487 vec![CallArg::Object(receiving_object_arg1)],
1488 );
1489
1490 execution_scheduler.enqueue_transactions(
1492 vec![(receive_object_transaction0.clone(), ExecutionEnv::new())],
1493 &state.epoch_store_for_testing(),
1494 );
1495 execution_scheduler.enqueue_transactions(
1496 vec![(receive_object_transaction01.clone(), ExecutionEnv::new())],
1497 &state.epoch_store_for_testing(),
1498 );
1499 execution_scheduler.enqueue_transactions(
1500 vec![(receive_object_transaction1.clone(), ExecutionEnv::new())],
1501 &state.epoch_store_for_testing(),
1502 );
1503 sleep(Duration::from_secs(1)).await;
1504 rx_ready_certificates.recv().await.unwrap();
1505 rx_ready_certificates.recv().await.unwrap();
1506 rx_ready_certificates.recv().await.unwrap();
1507 assert!(rx_ready_certificates.try_recv().is_err());
1508 }
1509
1510 #[sim_test]
1513 async fn execution_scheduler_with_cancelled_transactions() {
1514 let (owner, _keypair) = deterministic_random_account_key();
1516 let gas_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1517 let shared_object_1 = Object::shared_for_testing();
1518 let initial_shared_version_1 = shared_object_1.owner().start_version().unwrap();
1519 let shared_object_2 = Object::shared_for_testing();
1520 let initial_shared_version_2 = shared_object_2.owner().start_version().unwrap();
1521 let owned_object = Object::with_id_owner_for_testing(ObjectID::random(), owner);
1522
1523 let state = init_state_with_objects(vec![
1524 gas_object.clone(),
1525 shared_object_1.clone(),
1526 shared_object_2.clone(),
1527 owned_object.clone(),
1528 ])
1529 .await;
1530
1531 let (execution_scheduler, mut rx_ready_certificates) = make_execution_scheduler(&state);
1534 assert!(rx_ready_certificates.try_recv().is_err());
1536
1537 let shared_object_arg_1 = ObjectArg::SharedObject {
1539 id: shared_object_1.id(),
1540 initial_shared_version: initial_shared_version_1,
1541 mutability: SharedObjectMutability::Mutable,
1542 };
1543 let shared_object_arg_2 = ObjectArg::SharedObject {
1544 id: shared_object_2.id(),
1545 initial_shared_version: initial_shared_version_2,
1546 mutability: SharedObjectMutability::Mutable,
1547 };
1548
1549 let owned_version = 2000.into();
1551 let mut owned_ref = owned_object.compute_object_reference();
1552 owned_ref.1 = owned_version;
1553 let owned_object_arg = ObjectArg::ImmOrOwnedObject(owned_ref);
1554
1555 let cancelled_transaction = make_transaction(
1556 gas_object.clone(),
1557 vec![
1558 CallArg::Object(shared_object_arg_1),
1559 CallArg::Object(shared_object_arg_2),
1560 CallArg::Object(owned_object_arg),
1561 ],
1562 );
1563 let assigned_versions = vec![
1564 (
1565 (
1566 shared_object_1.id(),
1567 shared_object_1.owner().start_version().unwrap(),
1568 ),
1569 SequenceNumber::CANCELLED_READ,
1570 ),
1571 (
1572 (
1573 shared_object_2.id(),
1574 shared_object_2.owner().start_version().unwrap(),
1575 ),
1576 SequenceNumber::CONGESTED,
1577 ),
1578 ];
1579
1580 execution_scheduler.enqueue_transactions(
1581 vec![(
1582 cancelled_transaction.clone(),
1583 ExecutionEnv::new().with_assigned_versions(AssignedVersions::new_for_testing(
1584 assigned_versions,
1585 None,
1586 )),
1587 )],
1588 &state.epoch_store_for_testing(),
1589 );
1590
1591 sleep(Duration::from_secs(1)).await;
1593 assert!(rx_ready_certificates.try_recv().is_err());
1594
1595 assert_eq!(execution_scheduler.num_pending_certificates(), 1);
1596
1597 let mut new_owned_object = owned_object.clone();
1599 new_owned_object
1600 .data
1601 .try_as_move_mut()
1602 .unwrap()
1603 .increment_version_to(owned_version);
1604 state
1605 .get_cache_writer()
1606 .write_object_entry_for_test(new_owned_object);
1607
1608 let available_txn = rx_ready_certificates.recv().await.unwrap().certificate;
1610 assert_eq!(available_txn.digest(), cancelled_transaction.digest());
1611
1612 sleep(Duration::from_secs(1)).await;
1613 assert!(rx_ready_certificates.try_recv().is_err());
1614
1615 execution_scheduler.check_empty_for_testing().await;
1616 }
1617
1618 #[test]
1619 fn test_barrier_dependency_builder() {
1620 let make_transaction = |non_exclusive_writes: Vec<u32>, exclusive_writes: Vec<u32>| {
1621 assert!(
1622 non_exclusive_writes
1623 .iter()
1624 .all(|id| !exclusive_writes.contains(id))
1625 );
1626 assert!(
1627 exclusive_writes
1628 .iter()
1629 .all(|id| !non_exclusive_writes.contains(id))
1630 );
1631
1632 let non_exclusive_writes = non_exclusive_writes
1633 .into_iter()
1634 .map(|id| ObjectID::from_single_byte(id as u8));
1635 let exclusive_writes = exclusive_writes
1636 .into_iter()
1637 .map(|id| ObjectID::from_single_byte(id as u8));
1638 let mut builder = ProgrammableTransactionBuilder::new();
1639 for non_exclusive_write in non_exclusive_writes {
1640 builder
1641 .obj(ObjectArg::SharedObject {
1642 id: non_exclusive_write,
1643 initial_shared_version: SequenceNumber::new(),
1644 mutability: SharedObjectMutability::NonExclusiveWrite,
1645 })
1646 .unwrap();
1647 }
1648
1649 for exclusive_write in exclusive_writes {
1650 builder
1651 .obj(ObjectArg::SharedObject {
1652 id: exclusive_write,
1653 initial_shared_version: SequenceNumber::new(),
1654 mutability: SharedObjectMutability::Mutable,
1655 })
1656 .unwrap();
1657 }
1658
1659 let tx = TransactionKind::ProgrammableTransaction(builder.finish());
1660 let tx_data =
1661 TransactionData::new(tx, SuiAddress::default(), random_object_ref(), 1, 1);
1662 Transaction::from_data_and_signer(tx_data, vec![])
1663 };
1664
1665 {
1667 let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1668 let tx1 = make_transaction(vec![1], vec![]);
1669 let tx2 = make_transaction(vec![], vec![1]);
1670
1671 let tx1_deps =
1672 barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1673 let tx2_deps =
1674 barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1675 assert!(tx1_deps.is_empty());
1676 assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1677 }
1678
1679 {
1682 let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1683 let tx1 = make_transaction(vec![1, 2], vec![]);
1684 let tx2 = make_transaction(vec![], vec![1]);
1685 let tx3 = make_transaction(vec![], vec![2]);
1686
1687 let tx1_deps =
1688 barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1689 let tx2_deps =
1690 barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1691 let tx3_deps =
1692 barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1693 assert!(tx1_deps.is_empty());
1694 assert_eq!(Vec::from_iter(tx2_deps), vec![*tx1.digest()]);
1695 assert_eq!(Vec::from_iter(tx3_deps), vec![*tx1.digest()]);
1696 }
1697
1698 {
1700 let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1701 let tx1 = make_transaction(vec![1], vec![]);
1702 let tx2 = make_transaction(vec![2], vec![]);
1703 let tx3 = make_transaction(vec![], vec![1, 2]);
1704
1705 let tx1_deps =
1706 barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1707 let tx2_deps =
1708 barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1709 let tx3_deps =
1710 barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1711 assert!(tx1_deps.is_empty());
1712 assert!(tx2_deps.is_empty());
1713 assert_eq!(tx3_deps, BTreeSet::from([*tx1.digest(), *tx2.digest()]));
1714 }
1715
1716 {
1718 let mut barrier_dependency_builder = BarrierDependencyBuilder::new();
1719 let tx1 = make_transaction(vec![1], vec![]);
1720 let tx2 = make_transaction(vec![], vec![1]);
1721 let tx3 = make_transaction(vec![], vec![1]);
1722
1723 let tx1_deps =
1724 barrier_dependency_builder.process_tx(*tx1.digest(), tx1.transaction_data());
1725 let tx2_deps =
1726 barrier_dependency_builder.process_tx(*tx2.digest(), tx2.transaction_data());
1727 let tx3_deps =
1728 barrier_dependency_builder.process_tx(*tx3.digest(), tx3.transaction_data());
1729 assert!(tx1_deps.is_empty());
1730 assert_eq!(tx2_deps, BTreeSet::from([*tx1.digest()]));
1731 assert!(tx3_deps.is_empty());
1732 }
1733 }
1734}