1use mysten_common::ZipDebugEqIteratorExt;
5use mysten_common::debug_fatal;
6
7use crate::authority::AuthorityPerEpochStore;
8use crate::authority::authority_per_epoch_store::CancelConsensusCertificateReason;
9use crate::execution_cache::ObjectCacheRead;
10use either::Either;
11use std::collections::BTreeMap;
12use std::collections::HashMap;
13use std::collections::HashSet;
14use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
15use sui_types::SUI_CLOCK_OBJECT_ID;
16use sui_types::SUI_CLOCK_OBJECT_SHARED_VERSION;
17use sui_types::SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID;
18use sui_types::base_types::ConsensusObjectSequenceKey;
19use sui_types::base_types::ConsensusObjectVersion;
20use sui_types::base_types::ObjectID;
21use sui_types::base_types::SystemObjectVersions;
22use sui_types::base_types::TransactionDigest;
23use sui_types::committee::EpochId;
24use sui_types::crypto::RandomnessRound;
25use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
26use sui_types::executable_transaction::VerifiedExecutableTransaction;
27use sui_types::executable_transaction::VerifiedExecutableTransactionWithAliases;
28use sui_types::storage::{
29 ObjectKey, transaction_non_shared_input_object_keys, transaction_receiving_object_keys,
30};
31use sui_types::transaction::SharedObjectMutability;
32use sui_types::transaction::{SharedInputObject, TransactionDataAPI, TransactionKey};
33use sui_types::{
34 IMPLICITLY_READ_SYSTEM_OBJECTS, SUI_RANDOMNESS_STATE_OBJECT_ID, base_types::SequenceNumber,
35 error::SuiResult,
36};
37use tracing::trace;
38
39pub struct SharedObjVerManager {}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct AssignedVersions {
44 pub shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
45 pub system_object_versions: SystemObjectVersions,
54}
55
56impl AssignedVersions {
57 pub fn new(
58 shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
59 system_object_versions: SystemObjectVersions,
60 ) -> Self {
61 debug_assert!(
62 shared_object_versions
63 .iter()
64 .filter(|((id, _), version)| {
65 id.is_implicitly_read_system_object() && !version.is_cancelled()
66 })
67 .all(|((id, _), version)| {
68 system_object_versions
69 .get(id)
70 .is_none_or(|system_version| system_version.version == *version)
71 }),
72 "declared versions {shared_object_versions:?} disagree with {system_object_versions:?}"
73 );
74 Self {
75 shared_object_versions,
76 system_object_versions,
77 }
78 }
79
80 pub fn empty() -> Self {
81 Self::new(vec![], SystemObjectVersions::empty())
82 }
83
84 pub fn with_accumulator_version_for_testing(mut self, version: ConsensusObjectVersion) -> Self {
88 self.system_object_versions
89 .insert_for_testing(SUI_ACCUMULATOR_ROOT_OBJECT_ID, version);
90 self
91 }
92
93 #[cfg(test)]
96 pub fn new_for_testing(
97 shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
98 accumulator_version: Option<SequenceNumber>,
99 ) -> Self {
100 Self::new(
101 shared_object_versions,
102 SystemObjectVersions::from_map(
103 accumulator_version
104 .map(|v| {
105 (
106 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
107 ConsensusObjectVersion {
108 initial_shared_version: sui_types::object::OBJECT_START_VERSION,
109 version: v,
110 },
111 )
112 })
113 .into_iter()
114 .collect(),
115 ),
116 )
117 }
118
119 pub fn accumulator_version(&self) -> Option<SequenceNumber> {
121 self.system_object_versions
122 .get(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
123 .map(|v| v.version)
124 }
125
126 pub fn iter(&self) -> impl Iterator<Item = &(ConsensusObjectSequenceKey, SequenceNumber)> {
127 self.shared_object_versions.iter()
128 }
129
130 pub fn as_slice(&self) -> &[(ConsensusObjectSequenceKey, SequenceNumber)] {
131 &self.shared_object_versions
132 }
133}
134
135#[derive(Default, Debug, PartialEq, Eq)]
136pub struct AssignedTxAndVersions(pub Vec<(TransactionKey, AssignedVersions)>);
137
138impl AssignedTxAndVersions {
139 pub fn new(assigned_versions: Vec<(TransactionKey, AssignedVersions)>) -> Self {
140 Self(assigned_versions)
141 }
142
143 pub fn into_map(self) -> HashMap<TransactionKey, AssignedVersions> {
144 self.0.into_iter().collect()
145 }
146}
147
148#[derive(Clone)]
151pub enum Schedulable<T = VerifiedExecutableTransaction> {
152 Transaction(T),
153 RandomnessStateUpdate(EpochId, RandomnessRound),
154 AccumulatorSettlement(EpochId, u64 ),
155 ConsensusCommitPrologue(EpochId, u64 , u32 ),
156}
157
158impl From<VerifiedExecutableTransaction> for Schedulable<VerifiedExecutableTransaction> {
159 fn from(tx: VerifiedExecutableTransaction) -> Self {
160 Schedulable::Transaction(tx)
161 }
162}
163
164impl From<Schedulable<VerifiedExecutableTransactionWithAliases>>
165 for Schedulable<VerifiedExecutableTransaction>
166{
167 fn from(schedulable: Schedulable<VerifiedExecutableTransactionWithAliases>) -> Self {
168 match schedulable {
169 Schedulable::Transaction(tx) => Schedulable::Transaction(tx.into_tx()),
170 Schedulable::RandomnessStateUpdate(epoch, round) => {
171 Schedulable::RandomnessStateUpdate(epoch, round)
172 }
173 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
174 Schedulable::AccumulatorSettlement(epoch, checkpoint_height)
175 }
176 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
177 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index)
178 }
179 }
180 }
181}
182
183pub trait AsTx {
186 fn as_tx(&self) -> &VerifiedExecutableTransaction;
187}
188
189impl AsTx for VerifiedExecutableTransaction {
190 fn as_tx(&self) -> &VerifiedExecutableTransaction {
191 self
192 }
193}
194
195impl AsTx for &'_ VerifiedExecutableTransaction {
196 fn as_tx(&self) -> &VerifiedExecutableTransaction {
197 self
198 }
199}
200
201impl AsTx for VerifiedExecutableTransactionWithAliases {
202 fn as_tx(&self) -> &VerifiedExecutableTransaction {
203 self.tx()
204 }
205}
206
207impl AsTx for &'_ VerifiedExecutableTransactionWithAliases {
208 fn as_tx(&self) -> &VerifiedExecutableTransaction {
209 self.tx()
210 }
211}
212
213impl Schedulable<&'_ VerifiedExecutableTransaction> {
214 pub fn to_owned_schedulable(&self) -> Schedulable<VerifiedExecutableTransaction> {
216 match self {
217 Schedulable::Transaction(tx) => Schedulable::Transaction((*tx).clone()),
218 Schedulable::RandomnessStateUpdate(epoch, round) => {
219 Schedulable::RandomnessStateUpdate(*epoch, *round)
220 }
221 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
222 Schedulable::AccumulatorSettlement(*epoch, *checkpoint_height)
223 }
224 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
225 Schedulable::ConsensusCommitPrologue(*epoch, *round, *sub_dag_index)
226 }
227 }
228 }
229}
230
231impl<T> Schedulable<T> {
232 pub fn as_tx(&self) -> Option<&VerifiedExecutableTransaction>
233 where
234 T: AsTx,
235 {
236 match self {
237 Schedulable::Transaction(tx) => Some(tx.as_tx()),
238 Schedulable::RandomnessStateUpdate(_, _) => None,
239 Schedulable::AccumulatorSettlement(_, _) => None,
240 Schedulable::ConsensusCommitPrologue(_, _, _) => None,
241 }
242 }
243
244 pub fn shared_input_objects(
245 &self,
246 epoch_store: &AuthorityPerEpochStore,
247 ) -> impl Iterator<Item = SharedInputObject> + '_
248 where
249 T: AsTx,
250 {
251 match self {
252 Schedulable::Transaction(tx) => Either::Left(tx.as_tx().shared_input_objects()),
253 Schedulable::RandomnessStateUpdate(_, _) => {
254 Either::Right(std::iter::once(SharedInputObject {
255 id: SUI_RANDOMNESS_STATE_OBJECT_ID,
256 initial_shared_version: epoch_store
257 .epoch_start_config()
258 .randomness_obj_initial_shared_version()
259 .expect("randomness obj initial shared version should be set"),
260 mutability: SharedObjectMutability::Mutable,
261 }))
262 }
263 Schedulable::AccumulatorSettlement(_, _) => {
264 Either::Right(std::iter::once(SharedInputObject {
265 id: SUI_ACCUMULATOR_ROOT_OBJECT_ID,
266 initial_shared_version: epoch_store
267 .epoch_start_config()
268 .accumulator_root_obj_initial_shared_version()
269 .expect("accumulator root obj initial shared version should be set"),
270 mutability: SharedObjectMutability::Mutable,
271 }))
272 }
273 Schedulable::ConsensusCommitPrologue(_, _, _) => {
274 Either::Right(std::iter::once(SharedInputObject {
275 id: SUI_CLOCK_OBJECT_ID,
276 initial_shared_version: SUI_CLOCK_OBJECT_SHARED_VERSION,
277 mutability: SharedObjectMutability::Mutable,
278 }))
279 }
280 }
281 }
282
283 pub fn non_shared_input_object_keys(&self) -> Vec<ObjectKey>
284 where
285 T: AsTx,
286 {
287 match self {
288 Schedulable::Transaction(tx) => transaction_non_shared_input_object_keys(tx.as_tx())
289 .expect("Transaction input should have been verified"),
290 Schedulable::RandomnessStateUpdate(_, _) => vec![],
291 Schedulable::AccumulatorSettlement(_, _) => vec![],
292 Schedulable::ConsensusCommitPrologue(_, _, _) => vec![],
293 }
294 }
295
296 pub fn receiving_object_keys(&self) -> Vec<ObjectKey>
297 where
298 T: AsTx,
299 {
300 match self {
301 Schedulable::Transaction(tx) => transaction_receiving_object_keys(tx.as_tx()),
302 Schedulable::RandomnessStateUpdate(_, _) => vec![],
303 Schedulable::AccumulatorSettlement(_, _) => vec![],
304 Schedulable::ConsensusCommitPrologue(_, _, _) => vec![],
305 }
306 }
307
308 pub fn key(&self) -> TransactionKey
309 where
310 T: AsTx,
311 {
312 match self {
313 Schedulable::Transaction(tx) => tx.as_tx().key(),
314 Schedulable::RandomnessStateUpdate(epoch, round) => {
315 TransactionKey::RandomnessRound(*epoch, *round)
316 }
317 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
318 TransactionKey::AccumulatorSettlement(*epoch, *checkpoint_height)
319 }
320 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
321 TransactionKey::ConsensusCommitPrologue(*epoch, *round, *sub_dag_index)
322 }
323 }
324 }
325}
326
327#[must_use]
328#[derive(Default, Eq, PartialEq, Debug)]
329pub struct ConsensusSharedObjVerAssignment {
330 pub shared_input_next_versions: HashMap<ConsensusObjectSequenceKey, SequenceNumber>,
331 pub assigned_versions: AssignedTxAndVersions,
332}
333
334impl SharedObjVerManager {
335 pub fn assign_versions_from_consensus<'a, T>(
336 epoch_store: &AuthorityPerEpochStore,
337 cache_reader: &dyn ObjectCacheRead,
338 assignables: impl Iterator<Item = &'a Schedulable<T>> + Clone,
339 cancelled_txns: &BTreeMap<TransactionDigest, CancelConsensusCertificateReason>,
340 ) -> SuiResult<ConsensusSharedObjVerAssignment>
341 where
342 T: AsTx + 'a,
343 {
344 let mut shared_input_next_versions = get_or_init_versions(
345 assignables
346 .clone()
347 .flat_map(|a| a.shared_input_objects(epoch_store)),
348 epoch_store,
349 cache_reader,
350 )?;
351 let mut assigned_versions = Vec::new();
352 for assignable in assignables {
353 assert!(
354 !matches!(assignable, Schedulable::AccumulatorSettlement(_, _))
355 || epoch_store.accumulators_enabled(),
356 "AccumulatorSettlement should not be scheduled when accumulators are disabled"
357 );
358
359 let cert_assigned_versions = Self::assign_versions_for_certificate(
360 epoch_store,
361 assignable,
362 &mut shared_input_next_versions,
363 cancelled_txns,
364 );
365 assigned_versions.push((assignable.key(), cert_assigned_versions));
366 }
367
368 Ok(ConsensusSharedObjVerAssignment {
369 shared_input_next_versions,
370 assigned_versions: AssignedTxAndVersions::new(assigned_versions),
371 })
372 }
373
374 pub fn assign_versions_from_effects(
375 certs_and_effects: &[(
376 &VerifiedExecutableTransaction,
377 &TransactionEffects,
378 Option<SequenceNumber>,
380 )],
381 epoch_store: &AuthorityPerEpochStore,
382 cache_reader: &dyn ObjectCacheRead,
383 ) -> AssignedTxAndVersions {
384 let _ = get_or_init_versions(
392 certs_and_effects.iter().flat_map(|(cert, _, _)| {
393 cert.transaction_data().shared_input_objects().into_iter()
394 }),
395 epoch_store,
396 cache_reader,
397 );
398 let mut assigned_versions = Vec::new();
399 for (cert, effects, accumulator_version) in certs_and_effects {
400 let initial_version_map: BTreeMap<_, _> = cert
401 .transaction_data()
402 .shared_input_objects()
403 .into_iter()
404 .map(|input| input.into_id_and_version())
405 .collect();
406 let accessed_versions: BTreeMap<ObjectID, SequenceNumber> = effects
408 .accessed_consensus_objects()
409 .into_iter()
410 .map(|iso| iso.id_and_version())
411 .collect();
412 let cert_assigned_versions: Vec<_> = accessed_versions
413 .iter()
414 .filter_map(|(id, version)| {
415 let v = initial_version_map
416 .get(id)
417 .map(|initial_version| ((*id, *initial_version), *version));
418 if v.is_none() {
419 debug_assert!(
420 IMPLICITLY_READ_SYSTEM_OBJECTS.contains(id),
421 "accessed consensus object is neither a declared input nor a known implicitly read system object: \
422 accessed={accessed_versions:?} declared={initial_version_map:?}"
423 );
424 }
425 v
426 })
427 .collect();
428 if let (Some(effects_version), Some(sequenced_version)) = (
429 accessed_versions.get(&SUI_ACCUMULATOR_ROOT_OBJECT_ID),
430 accumulator_version,
431 ) && !effects_version.is_cancelled()
432 && effects_version != sequenced_version
433 {
434 debug_fatal!(
435 "accumulator root version from effects {:?} disagrees \
436 with the reconstructed accumulator version {:?} for tx {:?}",
437 effects_version,
438 sequenced_version,
439 cert.digest()
440 );
441 }
442 let mut system_object_versions: BTreeMap<_, _> =
443 implicitly_read_system_objects(epoch_store)
444 .into_iter()
445 .filter_map(|(id, initial_shared_version)| {
446 let version = *accessed_versions.get(&id)?;
447 (!version.is_cancelled()).then_some((
450 id,
451 ConsensusObjectVersion {
452 initial_shared_version,
453 version,
454 },
455 ))
456 })
457 .collect();
458 if let Some(version) = *accumulator_version {
466 let initial_shared_version = epoch_store
467 .epoch_start_config()
468 .accumulator_root_obj_initial_shared_version()
469 .expect(
470 "initial shared version must be known for an implicitly read system object",
471 );
472 system_object_versions.insert(
473 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
474 ConsensusObjectVersion {
475 initial_shared_version,
476 version,
477 },
478 );
479 }
480 let system_object_versions = SystemObjectVersions::from_map(system_object_versions);
481 let tx_key = cert.key();
482 trace!(
483 ?tx_key,
484 ?cert_assigned_versions,
485 ?system_object_versions,
486 "assigned consensus object versions from effects"
487 );
488 assigned_versions.push((
489 tx_key,
490 AssignedVersions::new(cert_assigned_versions, system_object_versions),
491 ));
492 }
493 AssignedTxAndVersions::new(assigned_versions)
494 }
495
496 pub fn assign_versions_for_certificate(
497 epoch_store: &AuthorityPerEpochStore,
498 assignable: &Schedulable<impl AsTx>,
499 shared_input_next_versions: &mut HashMap<ConsensusObjectSequenceKey, SequenceNumber>,
500 cancelled_txns: &BTreeMap<TransactionDigest, CancelConsensusCertificateReason>,
501 ) -> AssignedVersions {
502 let shared_input_objects: Vec<_> = assignable.shared_input_objects(epoch_store).collect();
503
504 let system_object_versions = SystemObjectVersions::from_map(
505 implicitly_read_system_objects(epoch_store)
506 .into_iter()
507 .map(|(id, initial_shared_version)| {
508 let version = *shared_input_next_versions
509 .get(&(id, initial_shared_version))
510 .expect(
511 "implicitly read system objects must be in shared_input_next_versions",
512 );
513 (
514 id,
515 ConsensusObjectVersion {
516 initial_shared_version,
517 version,
518 },
519 )
520 })
521 .collect(),
522 );
523
524 if shared_input_objects.is_empty() {
525 return AssignedVersions::new(vec![], system_object_versions);
527 }
528
529 let tx_key = assignable.key();
530
531 let cancellation_info = tx_key
533 .as_digest()
534 .and_then(|tx_digest| cancelled_txns.get(tx_digest));
535 let congested_objects_info: Option<HashSet<_>> =
536 if let Some(CancelConsensusCertificateReason::CongestionOnObjects(congested_objects)) =
537 &cancellation_info
538 {
539 Some(congested_objects.iter().cloned().collect())
540 } else {
541 None
542 };
543 let txn_cancelled = cancellation_info.is_some();
544
545 let mut input_object_keys = assignable.non_shared_input_object_keys();
546 let mut assigned_versions = Vec::with_capacity(shared_input_objects.len());
547 let mut is_exclusively_accessed_input = Vec::with_capacity(shared_input_objects.len());
548 let receiving_object_keys = assignable.receiving_object_keys();
550 input_object_keys.extend(receiving_object_keys);
551
552 if txn_cancelled {
553 for SharedInputObject {
556 id,
557 initial_shared_version,
558 ..
559 } in shared_input_objects.iter()
560 {
561 let assigned_version = match cancellation_info {
562 Some(CancelConsensusCertificateReason::CongestionOnObjects(_)) => {
563 if congested_objects_info
564 .as_ref()
565 .is_some_and(|info| info.contains(id))
566 {
567 SequenceNumber::CONGESTED
568 } else {
569 SequenceNumber::CANCELLED_READ
570 }
571 }
572 Some(CancelConsensusCertificateReason::DkgFailed) => {
573 if id == &SUI_RANDOMNESS_STATE_OBJECT_ID {
574 SequenceNumber::RANDOMNESS_UNAVAILABLE
575 } else {
576 SequenceNumber::CANCELLED_READ
577 }
578 }
579 None => unreachable!("cancelled transaction should have cancellation info"),
580 };
581 assigned_versions.push(((*id, *initial_shared_version), assigned_version));
582 is_exclusively_accessed_input.push(false);
583 }
584 } else {
585 for (
586 SharedInputObject {
587 id,
588 initial_shared_version,
589 mutability,
590 },
591 assigned_version,
592 ) in shared_input_objects.iter().map(|obj| {
593 (
594 obj,
595 *shared_input_next_versions
596 .get(&obj.id_and_version())
597 .unwrap(),
598 )
599 }) {
600 assigned_versions.push(((*id, *initial_shared_version), assigned_version));
601 input_object_keys.push(ObjectKey(*id, assigned_version));
602 is_exclusively_accessed_input.push(mutability.is_exclusive());
603 }
604 }
605
606 let next_version =
607 SequenceNumber::lamport_increment(input_object_keys.iter().map(|obj| obj.1));
608 assert!(
609 next_version.is_valid(),
610 "Assigned version must be valid. Got {:?}",
611 next_version
612 );
613
614 if !txn_cancelled {
615 assigned_versions
617 .iter()
618 .zip_debug_eq(is_exclusively_accessed_input)
619 .filter_map(|((id, _), mutable)| {
620 if mutable {
621 Some((*id, next_version))
622 } else {
623 None
624 }
625 })
626 .for_each(|(id, version)| {
627 assert!(
628 version.is_valid(),
629 "Assigned version must be a valid version."
630 );
631 shared_input_next_versions
632 .insert(id, version)
633 .expect("Object must exist in shared_input_next_versions.");
634 });
635 }
636
637 trace!(
638 ?tx_key,
639 ?assigned_versions,
640 ?next_version,
641 ?txn_cancelled,
642 "locking shared objects"
643 );
644
645 AssignedVersions::new(assigned_versions, system_object_versions)
646 }
647}
648
649fn implicitly_read_system_objects(
650 epoch_store: &AuthorityPerEpochStore,
651) -> Vec<ConsensusObjectSequenceKey> {
652 let epoch_start_config = epoch_store.epoch_start_config();
653 let mut objects = Vec::new();
654 if epoch_store.accumulators_enabled() {
655 objects.push((
656 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
657 epoch_start_config
658 .accumulator_root_obj_initial_shared_version()
659 .expect("accumulator root obj initial shared version should be set"),
660 ));
661 }
662 if epoch_store.protocol_config().enable_forwarding_addresses()
663 && let Some(initial_shared_version) =
664 epoch_start_config.forwarding_address_registry_obj_initial_shared_version()
665 {
666 objects.push((
667 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
668 initial_shared_version,
669 ));
670 }
671 objects.push((SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION));
672 objects
673}
674
675fn get_or_init_versions<'a>(
676 shared_input_objects: impl Iterator<Item = SharedInputObject> + 'a,
677 epoch_store: &AuthorityPerEpochStore,
678 cache_reader: &dyn ObjectCacheRead,
679) -> SuiResult<HashMap<ConsensusObjectSequenceKey, SequenceNumber>> {
680 let mut shared_input_objects: Vec<_> = shared_input_objects
681 .map(|so| so.into_id_and_version())
682 .collect();
683
684 shared_input_objects.extend(implicitly_read_system_objects(epoch_store));
685
686 shared_input_objects.sort();
687 shared_input_objects.dedup();
688
689 epoch_store.get_or_init_next_object_versions(&shared_input_objects, cache_reader)
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695 use sui_macros::sim_test;
696
697 use crate::authority::AuthorityState;
698 use crate::authority::shared_object_version_manager::{
699 ConsensusSharedObjVerAssignment, SharedObjVerManager,
700 };
701 use crate::authority::test_authority_builder::TestAuthorityBuilder;
702 use std::collections::{BTreeMap, HashMap};
703 use std::sync::Arc;
704 use sui_protocol_config::ProtocolConfig;
705 use sui_test_transaction_builder::TestTransactionBuilder;
706 use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress};
707 use sui_types::crypto::{RandomnessRound, get_account_key_pair};
708 use sui_types::digests::ObjectDigest;
709 use sui_types::effects::TestEffectsBuilder;
710 use sui_types::executable_transaction::{
711 CertificateProof, ExecutableTransaction, VerifiedExecutableTransaction,
712 };
713
714 use sui_types::object::Object;
715 use sui_types::transaction::{ObjectArg, SenderSignedData, VerifiedTransaction};
716
717 use sui_types::gas_coin::GAS;
718 use sui_types::transaction::FundsWithdrawalArg;
719 use sui_types::{SUI_ACCUMULATOR_ROOT_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
720
721 fn forwarding_enabled_config() -> ProtocolConfig {
722 let mut config = ProtocolConfig::get_for_max_version_UNSAFE();
723 config.set_create_forwarding_address_registry_for_testing(true);
724 config.set_enable_forwarding_addresses_for_testing(true);
725 config
726 }
727
728 fn assigned_versions_for_testing(
729 shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
730 accumulator_version: Option<SequenceNumber>,
731 forwarding_address_registry_version: Option<SequenceNumber>,
732 ) -> AssignedVersions {
733 let at_start_version = |v| ConsensusObjectVersion {
734 initial_shared_version: sui_types::object::OBJECT_START_VERSION,
735 version: v,
736 };
737 let versions = [
738 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, accumulator_version),
739 (
740 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
741 forwarding_address_registry_version,
742 ),
743 (SUI_CLOCK_OBJECT_ID, Some(SUI_CLOCK_OBJECT_SHARED_VERSION)),
746 ]
747 .into_iter()
748 .filter_map(|(id, version)| Some((id, at_start_version(version?))))
749 .collect();
750 AssignedVersions::new(
751 shared_object_versions,
752 SystemObjectVersions::from_map(versions),
753 )
754 }
755
756 #[tokio::test]
757 async fn test_clock_version_follows_consensus_commit_prologue() {
758 let authority = TestAuthorityBuilder::new().build().await;
759 let epoch_store = authority.epoch_store_for_testing();
760 let clock_key = (SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION);
761 let certs = [
762 generate_shared_objs_tx_with_gas_version(&[], 3),
763 generate_shared_objs_tx_with_gas_version(&[], 5),
764 ];
765 let assignables = [
766 Schedulable::Transaction(&certs[0]),
767 Schedulable::ConsensusCommitPrologue(epoch_store.epoch(), 1, 0),
768 Schedulable::Transaction(&certs[1]),
769 ];
770 let ConsensusSharedObjVerAssignment {
771 shared_input_next_versions,
772 assigned_versions,
773 } = SharedObjVerManager::assign_versions_from_consensus(
774 &epoch_store,
775 authority.get_object_cache_reader().as_ref(),
776 assignables.iter(),
777 &BTreeMap::new(),
778 )
779 .unwrap();
780 let clock_versions = assigned_versions
781 .0
782 .iter()
783 .map(|(_, versions)| {
784 versions
785 .system_object_versions
786 .get(&SUI_CLOCK_OBJECT_ID)
787 .unwrap()
788 .version
789 })
790 .collect::<Vec<_>>();
791 let next_clock_version = *shared_input_next_versions.get(&clock_key).unwrap();
792 assert!(next_clock_version > SUI_CLOCK_OBJECT_SHARED_VERSION);
793 assert_eq!(
796 clock_versions,
797 vec![
798 SUI_CLOCK_OBJECT_SHARED_VERSION,
799 SUI_CLOCK_OBJECT_SHARED_VERSION,
800 next_clock_version,
801 ]
802 );
803 assert_eq!(
804 assigned_versions.0[1].1.shared_object_versions,
805 vec![(clock_key, SUI_CLOCK_OBJECT_SHARED_VERSION)]
806 );
807 }
808
809 #[tokio::test]
810 async fn test_assign_versions_from_consensus_basic() {
811 let shared_object = Object::shared_for_testing();
812 let id = shared_object.id();
813 let init_shared_version = shared_object.owner.start_version().unwrap();
814 let authority = TestAuthorityBuilder::new()
815 .with_starting_objects(std::slice::from_ref(&shared_object))
816 .build()
817 .await;
818 let certs = [
819 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 3),
820 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, false)], 5),
821 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 9),
822 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 11),
823 ];
824 let epoch_store = authority.epoch_store_for_testing();
825 let assignables = certs
826 .iter()
827 .map(Schedulable::Transaction)
828 .collect::<Vec<_>>();
829 let ConsensusSharedObjVerAssignment {
830 shared_input_next_versions,
831 assigned_versions,
832 } = SharedObjVerManager::assign_versions_from_consensus(
833 &epoch_store,
834 authority.get_object_cache_reader().as_ref(),
835 assignables.iter(),
836 &BTreeMap::new(),
837 )
838 .unwrap();
839 assert_eq!(
841 epoch_store
842 .get_next_object_version(&id, init_shared_version)
843 .unwrap(),
844 init_shared_version
845 );
846 assert_eq!(
849 *shared_input_next_versions
850 .get(&(id, init_shared_version))
851 .unwrap(),
852 SequenceNumber::from_u64(12)
853 );
854 let expected_accumulator_version = SequenceNumber::from_u64(1);
859 assert_eq!(
860 assigned_versions.0,
861 vec![
862 (
863 certs[0].key(),
864 assigned_versions_for_testing(
865 vec![((id, init_shared_version), init_shared_version)],
866 Some(expected_accumulator_version),
867 None
868 )
869 ),
870 (
871 certs[1].key(),
872 assigned_versions_for_testing(
873 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
874 Some(expected_accumulator_version),
875 None
876 )
877 ),
878 (
879 certs[2].key(),
880 assigned_versions_for_testing(
881 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
882 Some(expected_accumulator_version),
883 None
884 )
885 ),
886 (
887 certs[3].key(),
888 assigned_versions_for_testing(
889 vec![((id, init_shared_version), SequenceNumber::from_u64(10))],
890 Some(expected_accumulator_version),
891 None
892 )
893 ),
894 ]
895 );
896 }
897
898 #[tokio::test]
899 async fn test_forwarding_registry_version_follows_consensus_order() {
900 let authority = TestAuthorityBuilder::new()
901 .with_protocol_config(forwarding_enabled_config())
902 .build()
903 .await;
904 let epoch_store = authority.epoch_store_for_testing();
905 let registry_initial_version = epoch_store
906 .epoch_start_config()
907 .forwarding_address_registry_obj_initial_shared_version()
908 .unwrap();
909 let registry_key = (
910 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
911 registry_initial_version,
912 );
913 let certs = [
914 generate_shared_objs_tx_with_gas_version(&[], 3),
915 generate_shared_objs_tx_with_gas_version(
916 &[(
917 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
918 registry_initial_version,
919 true,
920 )],
921 5,
922 ),
923 generate_shared_objs_tx_with_gas_version(&[], 1),
924 ];
925 let assignables = certs
926 .iter()
927 .map(Schedulable::Transaction)
928 .collect::<Vec<_>>();
929 let ConsensusSharedObjVerAssignment {
930 shared_input_next_versions,
931 assigned_versions,
932 } = SharedObjVerManager::assign_versions_from_consensus(
933 &epoch_store,
934 authority.get_object_cache_reader().as_ref(),
935 assignables.iter(),
936 &BTreeMap::new(),
937 )
938 .unwrap();
939 let registry_version = |version| ConsensusObjectVersion {
940 initial_shared_version: registry_initial_version,
941 version,
942 };
943 assert_eq!(
946 assigned_versions
947 .0
948 .iter()
949 .map(|(_, versions)| {
950 (
951 versions.shared_object_versions.clone(),
952 versions
953 .system_object_versions
954 .get(&SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID)
955 .unwrap(),
956 )
957 })
958 .collect::<Vec<_>>(),
959 vec![
960 (vec![], registry_version(registry_initial_version)),
961 (
962 vec![(registry_key, registry_initial_version)],
963 registry_version(registry_initial_version),
964 ),
965 (vec![], registry_version(SequenceNumber::from_u64(6))),
966 ]
967 );
968 assert_eq!(
969 shared_input_next_versions.get(®istry_key),
970 Some(&SequenceNumber::from_u64(6))
971 );
972 }
973
974 #[tokio::test]
975 async fn test_forwarding_registry_declared_by_cancelled_transaction() {
976 let authority = TestAuthorityBuilder::new()
977 .with_protocol_config(forwarding_enabled_config())
978 .build()
979 .await;
980 let epoch_store = authority.epoch_store_for_testing();
981 let registry_initial_version = epoch_store
982 .epoch_start_config()
983 .forwarding_address_registry_obj_initial_shared_version()
984 .unwrap();
985 let registry_key = (
986 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
987 registry_initial_version,
988 );
989 let certs = [
990 generate_shared_objs_tx_with_gas_version(
991 &[(
992 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
993 registry_initial_version,
994 true,
995 )],
996 5,
997 ),
998 generate_shared_objs_tx_with_gas_version(&[], 3),
999 ];
1000 let cancelled_txns = BTreeMap::from([(
1001 *certs[0].digest(),
1002 CancelConsensusCertificateReason::CongestionOnObjects(vec![
1003 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
1004 ]),
1005 )]);
1006 let assignables = certs
1007 .iter()
1008 .map(Schedulable::Transaction)
1009 .collect::<Vec<_>>();
1010 let ConsensusSharedObjVerAssignment {
1011 shared_input_next_versions,
1012 assigned_versions,
1013 } = SharedObjVerManager::assign_versions_from_consensus(
1014 &epoch_store,
1015 authority.get_object_cache_reader().as_ref(),
1016 assignables.iter(),
1017 &cancelled_txns,
1018 )
1019 .unwrap();
1020 let expected_version = SequenceNumber::from_u64(1);
1023 assert_eq!(
1024 assigned_versions.0,
1025 vec![
1026 (
1027 certs[0].key(),
1028 assigned_versions_for_testing(
1029 vec![(registry_key, SequenceNumber::CONGESTED)],
1030 Some(expected_version),
1031 Some(expected_version)
1032 )
1033 ),
1034 (
1035 certs[1].key(),
1036 assigned_versions_for_testing(
1037 vec![],
1038 Some(expected_version),
1039 Some(expected_version)
1040 )
1041 ),
1042 ]
1043 );
1044 assert_eq!(
1045 shared_input_next_versions.get(®istry_key),
1046 Some(®istry_initial_version)
1047 );
1048 }
1049
1050 #[tokio::test]
1051 async fn test_forwarding_registry_version_from_effects() {
1052 let authority = TestAuthorityBuilder::new()
1053 .with_protocol_config(forwarding_enabled_config())
1054 .build()
1055 .await;
1056 let epoch_store = authority.epoch_store_for_testing();
1057 let registry_initial_version = epoch_store
1058 .epoch_start_config()
1059 .forwarding_address_registry_obj_initial_shared_version()
1060 .unwrap();
1061 let registry_version = SequenceNumber::from_u64(4);
1062 let certs = [
1063 generate_shared_objs_tx_with_gas_version(&[], 3),
1064 generate_shared_objs_tx_with_gas_version(
1065 &[(
1066 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
1067 registry_initial_version,
1068 false,
1069 )],
1070 5,
1071 ),
1072 ];
1073 let effects = certs
1074 .iter()
1075 .map(|cert| {
1076 TestEffectsBuilder::new(cert.data())
1077 .with_shared_input_versions(BTreeMap::from([(
1078 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
1079 registry_version,
1080 )]))
1081 .build()
1082 })
1083 .collect::<Vec<_>>();
1084 let assigned_versions = SharedObjVerManager::assign_versions_from_effects(
1085 certs
1086 .iter()
1087 .zip_debug_eq(effects.iter())
1088 .map(|(cert, effect)| (cert, effect, None))
1089 .collect::<Vec<_>>()
1090 .as_slice(),
1091 &epoch_store,
1092 authority.get_object_cache_reader().as_ref(),
1093 );
1094 let system_object_versions = SystemObjectVersions::from_map(BTreeMap::from([(
1095 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
1096 ConsensusObjectVersion {
1097 initial_shared_version: registry_initial_version,
1098 version: registry_version,
1099 },
1100 )]));
1101 assert_eq!(
1102 assigned_versions.0,
1103 vec![
1104 (
1105 certs[0].key(),
1106 AssignedVersions::new(vec![], system_object_versions.clone())
1107 ),
1108 (
1109 certs[1].key(),
1110 AssignedVersions::new(
1111 vec![(
1112 (
1113 SUI_FORWARDING_ADDRESS_REGISTRY_OBJECT_ID,
1114 registry_initial_version
1115 ),
1116 registry_version
1117 )],
1118 system_object_versions
1119 )
1120 ),
1121 ]
1122 );
1123 }
1124
1125 #[tokio::test]
1126 async fn test_assign_versions_from_consensus_with_randomness() {
1127 let authority = TestAuthorityBuilder::new().build().await;
1128 let epoch_store = authority.epoch_store_for_testing();
1129 let randomness_obj_version = epoch_store
1130 .epoch_start_config()
1131 .randomness_obj_initial_shared_version()
1132 .unwrap();
1133 let certs = [
1134 VerifiedExecutableTransaction::new_system(
1135 VerifiedTransaction::new_randomness_state_update(
1136 epoch_store.epoch(),
1137 RandomnessRound::new(1),
1138 vec![],
1139 randomness_obj_version,
1140 ),
1141 epoch_store.epoch(),
1142 ),
1143 generate_shared_objs_tx_with_gas_version(
1144 &[(
1145 SUI_RANDOMNESS_STATE_OBJECT_ID,
1146 randomness_obj_version,
1147 false,
1149 )],
1150 3,
1151 ),
1152 generate_shared_objs_tx_with_gas_version(
1153 &[(
1154 SUI_RANDOMNESS_STATE_OBJECT_ID,
1155 randomness_obj_version,
1156 false,
1157 )],
1158 5,
1159 ),
1160 ];
1161 let assignables = certs
1162 .iter()
1163 .map(Schedulable::Transaction)
1164 .collect::<Vec<_>>();
1165 let ConsensusSharedObjVerAssignment {
1166 shared_input_next_versions,
1167 assigned_versions,
1168 } = SharedObjVerManager::assign_versions_from_consensus(
1169 &epoch_store,
1170 authority.get_object_cache_reader().as_ref(),
1171 assignables.iter(),
1172 &BTreeMap::new(),
1173 )
1174 .unwrap();
1175 assert_eq!(
1177 epoch_store
1178 .get_next_object_version(&SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version)
1179 .unwrap(),
1180 randomness_obj_version
1181 );
1182 let next_randomness_obj_version = randomness_obj_version.next();
1183 assert_eq!(
1184 *shared_input_next_versions
1185 .get(&(SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version))
1186 .unwrap(),
1187 next_randomness_obj_version
1189 );
1190 let expected_accumulator_version = SequenceNumber::from_u64(1);
1191 assert_eq!(
1192 assigned_versions.0,
1193 vec![
1194 (
1195 certs[0].key(),
1196 assigned_versions_for_testing(
1197 vec![(
1198 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1199 randomness_obj_version
1200 )],
1201 Some(expected_accumulator_version),
1202 None
1203 )
1204 ),
1205 (
1206 certs[1].key(),
1207 assigned_versions_for_testing(
1209 vec![(
1210 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1211 next_randomness_obj_version
1212 )],
1213 Some(expected_accumulator_version),
1214 None
1215 )
1216 ),
1217 (
1218 certs[2].key(),
1219 assigned_versions_for_testing(
1221 vec![(
1222 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1223 next_randomness_obj_version
1224 )],
1225 Some(expected_accumulator_version),
1226 None
1227 )
1228 ),
1229 ]
1230 );
1231 }
1232
1233 #[tokio::test]
1235 async fn test_assign_versions_from_consensus_with_cancellation() {
1236 let shared_object_1 = Object::shared_for_testing();
1237 let shared_object_2 = Object::shared_for_testing();
1238 let id1 = shared_object_1.id();
1239 let id2 = shared_object_2.id();
1240 let init_shared_version_1 = shared_object_1.owner.start_version().unwrap();
1241 let init_shared_version_2 = shared_object_2.owner.start_version().unwrap();
1242 let authority = TestAuthorityBuilder::new()
1243 .with_starting_objects(&[shared_object_1.clone(), shared_object_2.clone()])
1244 .build()
1245 .await;
1246 let randomness_obj_version = authority
1247 .epoch_store_for_testing()
1248 .epoch_start_config()
1249 .randomness_obj_initial_shared_version()
1250 .unwrap();
1251
1252 let certs = [
1267 generate_shared_objs_tx_with_gas_version(
1268 &[
1269 (id1, init_shared_version_1, true),
1270 (id2, init_shared_version_2, true),
1271 ],
1272 3,
1273 ),
1274 generate_shared_objs_tx_with_gas_version(
1275 &[
1276 (id1, init_shared_version_1, true),
1277 (id2, init_shared_version_2, true),
1278 ],
1279 5,
1280 ),
1281 generate_shared_objs_tx_with_gas_version(&[(id1, init_shared_version_1, true)], 1),
1282 generate_shared_objs_tx_with_gas_version(
1283 &[
1284 (id1, init_shared_version_1, true),
1285 (id2, init_shared_version_2, true),
1286 ],
1287 9,
1288 ),
1289 generate_shared_objs_tx_with_gas_version(
1290 &[
1291 (
1292 SUI_RANDOMNESS_STATE_OBJECT_ID,
1293 randomness_obj_version,
1294 false,
1295 ),
1296 (id2, init_shared_version_2, true),
1297 ],
1298 11,
1299 ),
1300 ];
1301 let epoch_store = authority.epoch_store_for_testing();
1302
1303 let cancelled_txns: BTreeMap<TransactionDigest, CancelConsensusCertificateReason> = [
1305 (
1306 *certs[1].digest(),
1307 CancelConsensusCertificateReason::CongestionOnObjects(vec![id1]),
1308 ),
1309 (
1310 *certs[3].digest(),
1311 CancelConsensusCertificateReason::CongestionOnObjects(vec![id2]),
1312 ),
1313 (
1314 *certs[4].digest(),
1315 CancelConsensusCertificateReason::DkgFailed,
1316 ),
1317 ]
1318 .into_iter()
1319 .collect();
1320
1321 let assignables = certs
1322 .iter()
1323 .map(Schedulable::Transaction)
1324 .collect::<Vec<_>>();
1325
1326 let ConsensusSharedObjVerAssignment {
1328 mut shared_input_next_versions,
1329 assigned_versions,
1330 } = SharedObjVerManager::assign_versions_from_consensus(
1331 &epoch_store,
1332 authority.get_object_cache_reader().as_ref(),
1333 assignables.iter(),
1334 &cancelled_txns,
1335 )
1336 .unwrap();
1337
1338 shared_input_next_versions
1341 .remove(&(SUI_ACCUMULATOR_ROOT_OBJECT_ID, SequenceNumber::from_u64(1)));
1342 shared_input_next_versions.remove(&(SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION));
1343 assert_eq!(
1344 shared_input_next_versions,
1345 HashMap::from([
1346 ((id1, init_shared_version_1), SequenceNumber::from_u64(5)), ((id2, init_shared_version_2), SequenceNumber::from_u64(4)), (
1349 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1350 SequenceNumber::from_u64(1)
1351 ), ])
1353 );
1354
1355 let expected_accumulator_version = SequenceNumber::from_u64(1);
1357 assert_eq!(
1358 assigned_versions.0,
1359 vec![
1360 (
1361 certs[0].key(),
1362 assigned_versions_for_testing(
1363 vec![
1364 ((id1, init_shared_version_1), init_shared_version_1),
1365 ((id2, init_shared_version_2), init_shared_version_2)
1366 ],
1367 Some(expected_accumulator_version),
1368 None
1369 )
1370 ),
1371 (
1372 certs[1].key(),
1373 assigned_versions_for_testing(
1374 vec![
1375 ((id1, init_shared_version_1), SequenceNumber::CONGESTED),
1376 ((id2, init_shared_version_2), SequenceNumber::CANCELLED_READ),
1377 ],
1378 Some(expected_accumulator_version),
1379 None
1380 )
1381 ),
1382 (
1383 certs[2].key(),
1384 assigned_versions_for_testing(
1385 vec![((id1, init_shared_version_1), SequenceNumber::from_u64(4))],
1386 Some(expected_accumulator_version),
1387 None
1388 )
1389 ),
1390 (
1391 certs[3].key(),
1392 assigned_versions_for_testing(
1393 vec![
1394 ((id1, init_shared_version_1), SequenceNumber::CANCELLED_READ),
1395 ((id2, init_shared_version_2), SequenceNumber::CONGESTED)
1396 ],
1397 Some(expected_accumulator_version),
1398 None
1399 )
1400 ),
1401 (
1402 certs[4].key(),
1403 assigned_versions_for_testing(
1404 vec![
1405 (
1406 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1407 SequenceNumber::RANDOMNESS_UNAVAILABLE
1408 ),
1409 ((id2, init_shared_version_2), SequenceNumber::CANCELLED_READ)
1410 ],
1411 Some(expected_accumulator_version),
1412 None
1413 )
1414 ),
1415 ]
1416 );
1417 }
1418
1419 #[sim_test]
1420 async fn test_assign_versions_from_effects() {
1421 let shared_object = Object::shared_for_testing();
1422 let id = shared_object.id();
1423 let init_shared_version = shared_object.owner.start_version().unwrap();
1424 let authority = TestAuthorityBuilder::new()
1425 .with_starting_objects(std::slice::from_ref(&shared_object))
1426 .build()
1427 .await;
1428 let certs = [
1429 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 3),
1430 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, false)], 5),
1431 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 9),
1432 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 11),
1433 ];
1434 let effects = [
1435 TestEffectsBuilder::new(certs[0].data()).build(),
1436 TestEffectsBuilder::new(certs[1].data())
1437 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(4))]))
1438 .build(),
1439 TestEffectsBuilder::new(certs[2].data())
1440 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(4))]))
1441 .build(),
1442 TestEffectsBuilder::new(certs[3].data())
1443 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(10))]))
1444 .build(),
1445 ];
1446 let epoch_store = authority.epoch_store_for_testing();
1447 let assigned_versions = SharedObjVerManager::assign_versions_from_effects(
1448 certs
1449 .iter()
1450 .zip_debug_eq(effects.iter())
1451 .map(|(cert, effect)| (cert, effect, None))
1452 .collect::<Vec<_>>()
1453 .as_slice(),
1454 &epoch_store,
1455 authority.get_object_cache_reader().as_ref(),
1456 );
1457 assert_eq!(
1459 epoch_store
1460 .get_next_object_version(&id, init_shared_version)
1461 .unwrap(),
1462 init_shared_version
1463 );
1464 assert_eq!(
1465 assigned_versions.0,
1466 vec![
1467 (
1468 certs[0].key(),
1469 AssignedVersions::new(
1470 vec![((id, init_shared_version), init_shared_version)],
1471 SystemObjectVersions::empty()
1472 )
1473 ),
1474 (
1475 certs[1].key(),
1476 AssignedVersions::new(
1477 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
1478 SystemObjectVersions::empty()
1479 )
1480 ),
1481 (
1482 certs[2].key(),
1483 AssignedVersions::new(
1484 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
1485 SystemObjectVersions::empty()
1486 )
1487 ),
1488 (
1489 certs[3].key(),
1490 AssignedVersions::new(
1491 vec![((id, init_shared_version), SequenceNumber::from_u64(10))],
1492 SystemObjectVersions::empty()
1493 )
1494 ),
1495 ]
1496 );
1497 }
1498
1499 fn generate_shared_objs_tx_with_gas_version(
1503 shared_objects: &[(ObjectID, SequenceNumber, bool)],
1504 gas_object_version: u64,
1505 ) -> VerifiedExecutableTransaction {
1506 let mut tx_builder = TestTransactionBuilder::new(
1507 SuiAddress::ZERO,
1508 (
1509 ObjectID::random(),
1510 SequenceNumber::from_u64(gas_object_version),
1511 ObjectDigest::random(),
1512 ),
1513 0,
1514 );
1515 let tx_data = {
1516 let builder = tx_builder.ptb_builder_mut();
1517 for (shared_object_id, shared_object_init_version, shared_object_mutable) in
1518 shared_objects
1519 {
1520 builder
1521 .obj(ObjectArg::SharedObject {
1522 id: *shared_object_id,
1523 initial_shared_version: *shared_object_init_version,
1524 mutability: if *shared_object_mutable {
1525 SharedObjectMutability::Mutable
1526 } else {
1527 SharedObjectMutability::Immutable
1528 },
1529 })
1530 .unwrap();
1531 }
1532 tx_builder.build()
1533 };
1534 let tx = SenderSignedData::new(tx_data, vec![]);
1535 VerifiedExecutableTransaction::new_unchecked(ExecutableTransaction::new_from_data_and_sig(
1536 tx,
1537 CertificateProof::new_system(0),
1538 ))
1539 }
1540
1541 struct WithdrawTestContext {
1542 authority: Arc<AuthorityState>,
1543 assignables: Vec<Schedulable<VerifiedExecutableTransaction>>,
1544 shared_objects: Vec<Object>,
1545 }
1546
1547 impl WithdrawTestContext {
1548 pub async fn new() -> Self {
1549 let shared_objects = vec![Object::shared_for_testing()];
1551 let mut config = ProtocolConfig::get_for_max_version_UNSAFE();
1552 config.set_enable_accumulators_for_testing(true);
1553 config.set_enable_forwarding_addresses_for_testing(false);
1554 let authority = TestAuthorityBuilder::new()
1555 .with_starting_objects(&shared_objects)
1556 .with_protocol_config(config)
1557 .build()
1558 .await;
1559 Self {
1560 authority,
1561 assignables: vec![],
1562 shared_objects,
1563 }
1564 }
1565
1566 pub fn add_withdraw_transaction(&mut self) -> TransactionKey {
1567 let (sender, keypair) = get_account_key_pair();
1569 let gas_object = Object::with_owner_for_testing(sender);
1570 let gas_object_ref = gas_object.compute_object_reference();
1571 let gas_price = (self.assignables.len() + 1) as u64;
1573 let mut tx_builder = TestTransactionBuilder::new(sender, gas_object_ref, gas_price);
1574 let tx_data = {
1575 let ptb_builder = tx_builder.ptb_builder_mut();
1576 ptb_builder
1577 .funds_withdrawal(FundsWithdrawalArg::balance_from_sender(
1578 200,
1579 GAS::type_tag(),
1580 ))
1581 .unwrap();
1582 tx_builder.build()
1583 };
1584 let cert = VerifiedExecutableTransaction::new_for_testing(tx_data, &keypair);
1585 let key = cert.key();
1586 self.assignables.push(Schedulable::Transaction(cert));
1587 key
1588 }
1589
1590 pub fn add_settlement_transaction(&mut self) -> TransactionKey {
1591 let height = (self.assignables.len() + 1) as u64;
1592 let settlement = Schedulable::AccumulatorSettlement(0, height);
1593 let key = settlement.key();
1594 self.assignables.push(settlement);
1595 key
1596 }
1597
1598 pub fn add_withdraw_with_shared_object_transaction(&mut self) -> TransactionKey {
1599 let (sender, keypair) = get_account_key_pair();
1601 let gas_object = Object::with_owner_for_testing(sender);
1602 let gas_object_ref = gas_object.compute_object_reference();
1603 let gas_price = (self.assignables.len() + 1) as u64;
1605 let mut tx_builder = TestTransactionBuilder::new(sender, gas_object_ref, gas_price);
1606 let tx_data = {
1607 let ptb_builder = tx_builder.ptb_builder_mut();
1608 if let Some(shared_obj) = self.shared_objects.first() {
1610 let id = shared_obj.id();
1611 let init_version = shared_obj.owner.start_version().unwrap();
1612 ptb_builder
1613 .obj(ObjectArg::SharedObject {
1614 id,
1615 initial_shared_version: init_version,
1616 mutability: SharedObjectMutability::Mutable,
1617 })
1618 .unwrap();
1619 }
1620 ptb_builder
1622 .funds_withdrawal(FundsWithdrawalArg::balance_from_sender(
1623 200,
1624 GAS::type_tag(),
1625 ))
1626 .unwrap();
1627 tx_builder.build()
1628 };
1629 let cert = VerifiedExecutableTransaction::new_for_testing(tx_data, &keypair);
1630 let key = cert.key();
1631 self.assignables.push(Schedulable::Transaction(cert));
1632 key
1633 }
1634
1635 pub fn assign_versions_from_consensus(&self) -> ConsensusSharedObjVerAssignment {
1636 let epoch_store = self.authority.epoch_store_for_testing();
1637 SharedObjVerManager::assign_versions_from_consensus(
1638 &epoch_store,
1639 self.authority.get_object_cache_reader().as_ref(),
1640 self.assignables.iter(),
1641 &BTreeMap::new(),
1642 )
1643 .unwrap()
1644 }
1645 }
1646
1647 #[sim_test]
1648 async fn test_assign_versions_from_consensus_with_withdraws_simple() {
1649 let mut ctx = WithdrawTestContext::new().await;
1652
1653 let acc_version = ctx
1654 .authority
1655 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1656 .unwrap()
1657 .version();
1658
1659 let withdraw_key = ctx.add_withdraw_transaction();
1660 let settlement_key = ctx.add_settlement_transaction();
1661
1662 let assigned_versions = ctx.assign_versions_from_consensus();
1663 assert_eq!(
1664 assigned_versions,
1665 ConsensusSharedObjVerAssignment {
1666 assigned_versions: AssignedTxAndVersions::new(vec![
1667 (
1668 withdraw_key,
1669 assigned_versions_for_testing(vec![], Some(acc_version), None)
1670 ),
1671 (
1672 settlement_key,
1673 assigned_versions_for_testing(
1674 vec![((SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version), acc_version)],
1675 Some(acc_version),
1676 None
1677 )
1678 ),
1679 ]),
1680 shared_input_next_versions: HashMap::from([
1681 (
1682 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1683 acc_version.next()
1684 ),
1685 (
1686 (SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION),
1687 SUI_CLOCK_OBJECT_SHARED_VERSION
1688 ),
1689 ]),
1690 }
1691 );
1692 }
1693
1694 #[sim_test]
1695 async fn test_assign_versions_from_consensus_with_multiple_withdraws_and_settlements() {
1696 let mut ctx = WithdrawTestContext::new().await;
1698
1699 let acc_version = ctx
1700 .authority
1701 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1702 .unwrap()
1703 .version();
1704
1705 let withdraw_key1 = ctx.add_withdraw_transaction();
1707 let settlement_key1 = ctx.add_settlement_transaction();
1708
1709 let withdraw_key2 = ctx.add_withdraw_transaction();
1711 let settlement_key2 = ctx.add_settlement_transaction();
1712
1713 let withdraw_key3 = ctx.add_withdraw_transaction();
1715 let settlement_key3 = ctx.add_settlement_transaction();
1716
1717 let assigned_versions = ctx.assign_versions_from_consensus();
1718 assert_eq!(
1719 assigned_versions,
1720 ConsensusSharedObjVerAssignment {
1721 assigned_versions: AssignedTxAndVersions::new(vec![
1722 (
1723 withdraw_key1,
1724 assigned_versions_for_testing(vec![], Some(acc_version), None)
1725 ),
1726 (
1727 settlement_key1,
1728 assigned_versions_for_testing(
1729 vec![((SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version), acc_version)],
1730 Some(acc_version),
1731 None
1732 )
1733 ),
1734 (
1735 withdraw_key2,
1736 assigned_versions_for_testing(vec![], Some(acc_version.next()), None)
1737 ),
1738 (
1739 settlement_key2,
1740 assigned_versions_for_testing(
1741 vec![(
1742 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1743 acc_version.next()
1744 )],
1745 Some(acc_version.next()),
1746 None
1747 )
1748 ),
1749 (
1750 withdraw_key3,
1751 assigned_versions_for_testing(
1752 vec![],
1753 Some(acc_version.next().next()),
1754 None
1755 )
1756 ),
1757 (
1758 settlement_key3,
1759 assigned_versions_for_testing(
1760 vec![(
1761 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1762 acc_version.next().next()
1763 )],
1764 Some(acc_version.next().next()),
1765 None
1766 )
1767 ),
1768 ]),
1769 shared_input_next_versions: HashMap::from([
1770 (
1771 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1772 acc_version.next().next().next()
1773 ),
1774 (
1775 (SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION),
1776 SUI_CLOCK_OBJECT_SHARED_VERSION
1777 ),
1778 ]),
1779 }
1780 );
1781 }
1782
1783 #[sim_test]
1784 async fn test_assign_versions_from_consensus_with_withdraw_and_shared_object() {
1785 let mut ctx = WithdrawTestContext::new().await;
1787
1788 let shared_obj_id = ctx.shared_objects[0].id();
1790 let shared_obj_version = ctx.shared_objects[0].owner.start_version().unwrap();
1791
1792 let acc_version = ctx
1793 .authority
1794 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1795 .unwrap()
1796 .version();
1797
1798 let withdraw_with_shared_key = ctx.add_withdraw_with_shared_object_transaction();
1799 let settlement_key = ctx.add_settlement_transaction();
1800
1801 let assigned_versions = ctx.assign_versions_from_consensus();
1802 assert_eq!(
1803 assigned_versions,
1804 ConsensusSharedObjVerAssignment {
1805 assigned_versions: AssignedTxAndVersions::new(vec![
1806 (
1807 withdraw_with_shared_key,
1808 assigned_versions_for_testing(
1809 vec![((shared_obj_id, shared_obj_version), shared_obj_version)],
1810 Some(acc_version),
1811 None
1812 )
1813 ),
1814 (
1815 settlement_key,
1816 assigned_versions_for_testing(
1817 vec![((SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version), acc_version)],
1818 Some(acc_version),
1819 None
1820 )
1821 ),
1822 ]),
1823 shared_input_next_versions: HashMap::from([
1824 (
1825 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1826 acc_version.next()
1827 ),
1828 (
1829 (shared_obj_id, shared_obj_version),
1830 shared_obj_version.next()
1831 ),
1832 (
1833 (SUI_CLOCK_OBJECT_ID, SUI_CLOCK_OBJECT_SHARED_VERSION),
1834 SUI_CLOCK_OBJECT_SHARED_VERSION
1835 ),
1836 ]),
1837 }
1838 );
1839 }
1840}