Skip to main content

sui_core/authority/
shared_object_version_manager.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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/// Version assignments for a single transaction
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct AssignedVersions {
44    pub shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
45    /// Versions of system objects, keyed by object ID, that this transaction may read during
46    /// execution but that are not part of its declared shared inputs. Each version is assigned
47    /// deterministically during consensus sequencing, so that every validator reads the same
48    /// version of the object.
49    ///
50    /// Today this holds the accumulator root version, the Clock version and, when forwarding
51    /// addresses are enabled, the forwarding address registry version, each as of the point in the
52    /// consensus commit at which this transaction is sequenced.
53    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    /// Sets the accumulator root as a system object read during execution. For test paths
85    /// that execute directly: the test version-assignment helper assigns no root version,
86    /// but execution reads the root implicitly for any object funds withdraw.
87    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    /// Construct with only the accumulator root as the system object read during execution.
94    /// Production callers build the full `system_object_versions` map directly.
95    #[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    /// The accumulator root version this transaction reads, if any.
120    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/// A wrapper around things that can be scheduled for execution by the assigning of
149/// shared object versions.
150#[derive(Clone)]
151pub enum Schedulable<T = VerifiedExecutableTransaction> {
152    Transaction(T),
153    RandomnessStateUpdate(EpochId, RandomnessRound),
154    AccumulatorSettlement(EpochId, u64 /* checkpoint height */),
155    ConsensusCommitPrologue(EpochId, u64 /* round */, u32 /* sub_dag_index */),
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
183// AsTx is like Deref, in that it allows us to use either refs or values in Schedulable.
184// Deref does not work because it conflicts with the impl of Deref for VerifiedExecutableTransaction.
185pub 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    // Cannot use the blanket ToOwned trait impl because it just calls clone.
215    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            // Accumulator version
379            Option<SequenceNumber>,
380        )],
381        epoch_store: &AuthorityPerEpochStore,
382        cache_reader: &dyn ObjectCacheRead,
383    ) -> AssignedTxAndVersions {
384        // We don't care about the results since we can use effects to assign versions.
385        // But we must call it to make sure whenever a consensus object is touched the first time
386        // during an epoch, either through consensus or through checkpoint executor,
387        // its next version must be initialized. This is because we initialize the next version
388        // of a consensus object in an epoch by reading the current version from the object store.
389        // This must be done before we mutate it the first time, otherwise we would be initializing
390        // it with the wrong version.
391        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            // Implicitly read system objects retrieve their accessed versions from this map.
407            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                        // A cancelled sentinel only appears when the object was also a declared
448                        // input of a cancelled transaction; it is not a version to pin.
449                        (!version.is_cancelled()).then_some((
450                            id,
451                            ConsensusObjectVersion {
452                                initial_shared_version,
453                                version,
454                            },
455                        ))
456                    })
457                    .collect();
458            // Note that for accumulator version, we cannot rely on the one from effects yet, since it won't
459            // be produced until implicitly read system objects are fully shipped. But the old object funds withdraw
460            // still need it. Hence we always use the one provided from the caller (i.e. checkpoint executor).
461            // TODO: Once check_object_funds_withdraw_in_execution is enabled everywhere and the
462            // post-execution ObjectFundsChecker is removed, drop this override (and the
463            // accumulator version threaded from the checkpoint executor) so the accumulator root
464            // is reconstructed from effects exactly like the forwarding address registry.
465            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            // No shared object used by this transaction. No need to assign versions.
526            return AssignedVersions::new(vec![], system_object_versions);
527        }
528
529        let tx_key = assignable.key();
530
531        // Check if the transaction is cancelled due to congestion.
532        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        // Record receiving object versions towards the shared version computation.
549        let receiving_object_keys = assignable.receiving_object_keys();
550        input_object_keys.extend(receiving_object_keys);
551
552        if txn_cancelled {
553            // For cancelled transaction due to congestion, assign special versions to all shared objects.
554            // Note that new lamport version does not depend on any shared objects.
555            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            // Update the next version for the shared objects.
616            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            // Only a consensus commit prologue advances the Clock, and these tests sequence
744            // none, so the Clock stays at its genesis version.
745            (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        // The transaction sequenced before the prologue reads the genesis Clock; the one after it
794        // reads the Clock the prologue writes.
795        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        // Check that the shared object's next version is always initialized in the epoch store.
840        assert_eq!(
841            epoch_store
842                .get_next_object_version(&id, init_shared_version)
843                .unwrap(),
844            init_shared_version
845        );
846        // Check that the final version of the shared object is the lamport version of the last
847        // transaction.
848        assert_eq!(
849            *shared_input_next_versions
850                .get(&(id, init_shared_version))
851                .unwrap(),
852            SequenceNumber::from_u64(12)
853        );
854        // Check that the version assignment for each transaction is correct.
855        // For a transaction that uses the shared object with mutable=false, it won't update the version
856        // using lamport version, hence the next transaction will use the same version number.
857        // In the following case, certs[2] has the same assignment as certs[1] for this reason.
858        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        // The registration is sequenced second, so only the last transaction reads the
944        // registry at its post-registration version.
945        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(&registry_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        // The cancelled declaration carries the sentinel, while the implicit pin keeps the real
1021        // version and the registry does not advance for the transaction after it.
1022        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(&registry_key),
1046            Some(&registry_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                    // This can only be false since it's not allowed to use randomness object with mutable=true.
1148                    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        // Check that the randomness object's next version is initialized.
1176        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            // Randomness object's version is only incremented by 1 regardless of lamport version.
1188            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                    // It is critical that the randomness object version is updated before the assignment.
1208                    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                    // It is critical that the randomness object version is updated before the assignment.
1220                    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    // Tests shared object version assignment for cancelled transaction.
1234    #[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        // Generate 5 transactions for testing.
1253        //   tx1: shared_object_1, shared_object_2, owned_object_version = 3
1254        //   tx2: shared_object_1, shared_object_2, owned_object_version = 5
1255        //   tx3: shared_object_1, owned_object_version = 1
1256        //   tx4: shared_object_1, shared_object_2, owned_object_version = 9
1257        //   tx5: shared_object_1, shared_object_2, owned_object_version = 11
1258        //
1259        // Later, we cancel transaction 2 and 4 due to congestion, and 5 due to DKG failure.
1260        // Expected outcome:
1261        //   tx1: both shared objects assign version 1, lamport version = 4
1262        //   tx2: shared objects assign cancelled version, lamport version = 6 due to gas object version = 5
1263        //   tx3: shared object 1 assign version 4, lamport version = 5
1264        //   tx4: shared objects assign cancelled version, lamport version = 10 due to gas object version = 9
1265        //   tx5: shared objects assign cancelled version, lamport version = 12 due to gas object version = 11
1266        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        // Cancel transactions 2 and 4 due to congestion.
1304        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        // Run version assignment logic.
1327        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        // Check that the final version of the shared object is the lamport version of the last
1339        // transaction.
1340        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)), // determined by tx3
1347                ((id2, init_shared_version_2), SequenceNumber::from_u64(4)), // determined by tx1
1348                (
1349                    (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
1350                    SequenceNumber::from_u64(1)
1351                ), // not mutable
1352            ])
1353        );
1354
1355        // Check that the version assignment for each transaction is correct.
1356        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        // Check that the shared object's next version is always initialized in the epoch store.
1458        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    /// Generate a transaction that uses shared objects as specified in the parameters.
1500    /// Also uses a gas object with specified version.
1501    /// The version of the gas object is used to manipulate the lamport version of this transaction.
1502    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            // Create a shared object for testing
1550            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            // Generate random sender and gas object for each transaction
1568            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            // Generate a unique gas price to make the transaction unique.
1572            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            // Generate random sender and gas object for each transaction
1600            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            // Generate a unique gas price to make the transaction unique.
1604            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                // Add shared object to the transaction
1609                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                // Add balance withdraw
1621                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        // Note that we don't need a shared object to trigger withdraw version assignment.
1650        // In fact it is important that this works without a shared object.
1651        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        // Test with multiple withdrawals and multiple settlements, with settlement as the last transaction
1697        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        // First withdrawal and settlement
1706        let withdraw_key1 = ctx.add_withdraw_transaction();
1707        let settlement_key1 = ctx.add_settlement_transaction();
1708
1709        // Second withdrawal and settlement
1710        let withdraw_key2 = ctx.add_withdraw_transaction();
1711        let settlement_key2 = ctx.add_settlement_transaction();
1712
1713        // Third withdrawal and final settlement
1714        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        // Test that a transaction can have both a withdrawal and use a shared object
1786        let mut ctx = WithdrawTestContext::new().await;
1787
1788        // Get the shared object info from the context
1789        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}