1use mysten_common::ZipDebugEqIteratorExt;
5
6use crate::authority::AuthorityPerEpochStore;
7use crate::authority::authority_per_epoch_store::CancelConsensusCertificateReason;
8use crate::execution_cache::ObjectCacheRead;
9use either::Either;
10use std::collections::BTreeMap;
11use std::collections::HashMap;
12use std::collections::HashSet;
13use sui_types::SUI_ACCUMULATOR_ROOT_OBJECT_ID;
14use sui_types::SUI_CLOCK_OBJECT_ID;
15use sui_types::SUI_CLOCK_OBJECT_SHARED_VERSION;
16use sui_types::base_types::ConsensusObjectSequenceKey;
17use sui_types::base_types::TransactionDigest;
18use sui_types::committee::EpochId;
19use sui_types::crypto::RandomnessRound;
20use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
21use sui_types::executable_transaction::VerifiedExecutableTransaction;
22use sui_types::executable_transaction::VerifiedExecutableTransactionWithAliases;
23use sui_types::storage::{
24 ObjectKey, transaction_non_shared_input_object_keys, transaction_receiving_object_keys,
25};
26use sui_types::transaction::SharedObjectMutability;
27use sui_types::transaction::{SharedInputObject, TransactionDataAPI, TransactionKey};
28use sui_types::{SUI_RANDOMNESS_STATE_OBJECT_ID, base_types::SequenceNumber, error::SuiResult};
29use tracing::trace;
30
31use super::epoch_start_configuration::EpochStartConfigTrait;
32
33pub struct SharedObjVerManager {}
34
35#[derive(Debug, Clone, Default, PartialEq, Eq)]
37pub struct AssignedVersions {
38 pub shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
39 pub accumulator_version: Option<SequenceNumber>,
48}
49
50impl AssignedVersions {
51 pub fn new(
52 shared_object_versions: Vec<(ConsensusObjectSequenceKey, SequenceNumber)>,
53 accumulator_version: Option<SequenceNumber>,
54 ) -> Self {
55 Self {
56 shared_object_versions,
57 accumulator_version,
58 }
59 }
60
61 pub fn iter(&self) -> impl Iterator<Item = &(ConsensusObjectSequenceKey, SequenceNumber)> {
62 self.shared_object_versions.iter()
63 }
64
65 pub fn as_slice(&self) -> &[(ConsensusObjectSequenceKey, SequenceNumber)] {
66 &self.shared_object_versions
67 }
68}
69
70#[derive(Default, Debug, PartialEq, Eq)]
71pub struct AssignedTxAndVersions(pub Vec<(TransactionKey, AssignedVersions)>);
72
73impl AssignedTxAndVersions {
74 pub fn new(assigned_versions: Vec<(TransactionKey, AssignedVersions)>) -> Self {
75 Self(assigned_versions)
76 }
77
78 pub fn into_map(self) -> HashMap<TransactionKey, AssignedVersions> {
79 self.0.into_iter().collect()
80 }
81}
82
83#[derive(Clone)]
86pub enum Schedulable<T = VerifiedExecutableTransaction> {
87 Transaction(T),
88 RandomnessStateUpdate(EpochId, RandomnessRound),
89 AccumulatorSettlement(EpochId, u64 ),
90 ConsensusCommitPrologue(EpochId, u64 , u32 ),
91}
92
93impl From<VerifiedExecutableTransaction> for Schedulable<VerifiedExecutableTransaction> {
94 fn from(tx: VerifiedExecutableTransaction) -> Self {
95 Schedulable::Transaction(tx)
96 }
97}
98
99impl From<Schedulable<VerifiedExecutableTransactionWithAliases>>
100 for Schedulable<VerifiedExecutableTransaction>
101{
102 fn from(schedulable: Schedulable<VerifiedExecutableTransactionWithAliases>) -> Self {
103 match schedulable {
104 Schedulable::Transaction(tx) => Schedulable::Transaction(tx.into_tx()),
105 Schedulable::RandomnessStateUpdate(epoch, round) => {
106 Schedulable::RandomnessStateUpdate(epoch, round)
107 }
108 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
109 Schedulable::AccumulatorSettlement(epoch, checkpoint_height)
110 }
111 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
112 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index)
113 }
114 }
115 }
116}
117
118pub trait AsTx {
121 fn as_tx(&self) -> &VerifiedExecutableTransaction;
122}
123
124impl AsTx for VerifiedExecutableTransaction {
125 fn as_tx(&self) -> &VerifiedExecutableTransaction {
126 self
127 }
128}
129
130impl AsTx for &'_ VerifiedExecutableTransaction {
131 fn as_tx(&self) -> &VerifiedExecutableTransaction {
132 self
133 }
134}
135
136impl AsTx for VerifiedExecutableTransactionWithAliases {
137 fn as_tx(&self) -> &VerifiedExecutableTransaction {
138 self.tx()
139 }
140}
141
142impl AsTx for &'_ VerifiedExecutableTransactionWithAliases {
143 fn as_tx(&self) -> &VerifiedExecutableTransaction {
144 self.tx()
145 }
146}
147
148impl Schedulable<&'_ VerifiedExecutableTransaction> {
149 pub fn to_owned_schedulable(&self) -> Schedulable<VerifiedExecutableTransaction> {
151 match self {
152 Schedulable::Transaction(tx) => Schedulable::Transaction((*tx).clone()),
153 Schedulable::RandomnessStateUpdate(epoch, round) => {
154 Schedulable::RandomnessStateUpdate(*epoch, *round)
155 }
156 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
157 Schedulable::AccumulatorSettlement(*epoch, *checkpoint_height)
158 }
159 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
160 Schedulable::ConsensusCommitPrologue(*epoch, *round, *sub_dag_index)
161 }
162 }
163 }
164}
165
166impl<T> Schedulable<T> {
167 pub fn as_tx(&self) -> Option<&VerifiedExecutableTransaction>
168 where
169 T: AsTx,
170 {
171 match self {
172 Schedulable::Transaction(tx) => Some(tx.as_tx()),
173 Schedulable::RandomnessStateUpdate(_, _) => None,
174 Schedulable::AccumulatorSettlement(_, _) => None,
175 Schedulable::ConsensusCommitPrologue(_, _, _) => None,
176 }
177 }
178
179 pub fn shared_input_objects(
180 &self,
181 epoch_store: &AuthorityPerEpochStore,
182 ) -> impl Iterator<Item = SharedInputObject> + '_
183 where
184 T: AsTx,
185 {
186 match self {
187 Schedulable::Transaction(tx) => Either::Left(tx.as_tx().shared_input_objects()),
188 Schedulable::RandomnessStateUpdate(_, _) => {
189 Either::Right(std::iter::once(SharedInputObject {
190 id: SUI_RANDOMNESS_STATE_OBJECT_ID,
191 initial_shared_version: epoch_store
192 .epoch_start_config()
193 .randomness_obj_initial_shared_version()
194 .expect("randomness obj initial shared version should be set"),
195 mutability: SharedObjectMutability::Mutable,
196 }))
197 }
198 Schedulable::AccumulatorSettlement(_, _) => {
199 Either::Right(std::iter::once(SharedInputObject {
200 id: SUI_ACCUMULATOR_ROOT_OBJECT_ID,
201 initial_shared_version: epoch_store
202 .epoch_start_config()
203 .accumulator_root_obj_initial_shared_version()
204 .expect("accumulator root obj initial shared version should be set"),
205 mutability: SharedObjectMutability::Mutable,
206 }))
207 }
208 Schedulable::ConsensusCommitPrologue(_, _, _) => {
209 Either::Right(std::iter::once(SharedInputObject {
210 id: SUI_CLOCK_OBJECT_ID,
211 initial_shared_version: SUI_CLOCK_OBJECT_SHARED_VERSION,
212 mutability: SharedObjectMutability::Mutable,
213 }))
214 }
215 }
216 }
217
218 pub fn non_shared_input_object_keys(&self) -> Vec<ObjectKey>
219 where
220 T: AsTx,
221 {
222 match self {
223 Schedulable::Transaction(tx) => transaction_non_shared_input_object_keys(tx.as_tx())
224 .expect("Transaction input should have been verified"),
225 Schedulable::RandomnessStateUpdate(_, _) => vec![],
226 Schedulable::AccumulatorSettlement(_, _) => vec![],
227 Schedulable::ConsensusCommitPrologue(_, _, _) => vec![],
228 }
229 }
230
231 pub fn receiving_object_keys(&self) -> Vec<ObjectKey>
232 where
233 T: AsTx,
234 {
235 match self {
236 Schedulable::Transaction(tx) => transaction_receiving_object_keys(tx.as_tx()),
237 Schedulable::RandomnessStateUpdate(_, _) => vec![],
238 Schedulable::AccumulatorSettlement(_, _) => vec![],
239 Schedulable::ConsensusCommitPrologue(_, _, _) => vec![],
240 }
241 }
242
243 pub fn key(&self) -> TransactionKey
244 where
245 T: AsTx,
246 {
247 match self {
248 Schedulable::Transaction(tx) => tx.as_tx().key(),
249 Schedulable::RandomnessStateUpdate(epoch, round) => {
250 TransactionKey::RandomnessRound(*epoch, *round)
251 }
252 Schedulable::AccumulatorSettlement(epoch, checkpoint_height) => {
253 TransactionKey::AccumulatorSettlement(*epoch, *checkpoint_height)
254 }
255 Schedulable::ConsensusCommitPrologue(epoch, round, sub_dag_index) => {
256 TransactionKey::ConsensusCommitPrologue(*epoch, *round, *sub_dag_index)
257 }
258 }
259 }
260}
261
262#[must_use]
263#[derive(Default, Eq, PartialEq, Debug)]
264pub struct ConsensusSharedObjVerAssignment {
265 pub shared_input_next_versions: HashMap<ConsensusObjectSequenceKey, SequenceNumber>,
266 pub assigned_versions: AssignedTxAndVersions,
267}
268
269impl SharedObjVerManager {
270 pub fn assign_versions_from_consensus<'a, T>(
271 epoch_store: &AuthorityPerEpochStore,
272 cache_reader: &dyn ObjectCacheRead,
273 assignables: impl Iterator<Item = &'a Schedulable<T>> + Clone,
274 cancelled_txns: &BTreeMap<TransactionDigest, CancelConsensusCertificateReason>,
275 ) -> SuiResult<ConsensusSharedObjVerAssignment>
276 where
277 T: AsTx + 'a,
278 {
279 let mut shared_input_next_versions = get_or_init_versions(
280 assignables
281 .clone()
282 .flat_map(|a| a.shared_input_objects(epoch_store)),
283 epoch_store,
284 cache_reader,
285 )?;
286 let mut assigned_versions = Vec::new();
287 for assignable in assignables {
288 assert!(
289 !matches!(assignable, Schedulable::AccumulatorSettlement(_, _))
290 || epoch_store.accumulators_enabled(),
291 "AccumulatorSettlement should not be scheduled when accumulators are disabled"
292 );
293
294 let cert_assigned_versions = Self::assign_versions_for_certificate(
295 epoch_store,
296 assignable,
297 &mut shared_input_next_versions,
298 cancelled_txns,
299 );
300 assigned_versions.push((assignable.key(), cert_assigned_versions));
301 }
302
303 Ok(ConsensusSharedObjVerAssignment {
304 shared_input_next_versions,
305 assigned_versions: AssignedTxAndVersions::new(assigned_versions),
306 })
307 }
308
309 pub fn assign_versions_from_effects(
310 certs_and_effects: &[(
311 &VerifiedExecutableTransaction,
312 &TransactionEffects,
313 Option<SequenceNumber>,
315 )],
316 epoch_store: &AuthorityPerEpochStore,
317 cache_reader: &dyn ObjectCacheRead,
318 ) -> AssignedTxAndVersions {
319 let _ = get_or_init_versions(
327 certs_and_effects.iter().flat_map(|(cert, _, _)| {
328 cert.transaction_data().shared_input_objects().into_iter()
329 }),
330 epoch_store,
331 cache_reader,
332 );
333 let mut assigned_versions = Vec::new();
334 for (cert, effects, accumulator_version) in certs_and_effects {
335 let initial_version_map: BTreeMap<_, _> = cert
336 .transaction_data()
337 .shared_input_objects()
338 .into_iter()
339 .map(|input| input.into_id_and_version())
340 .collect();
341 let cert_assigned_versions: Vec<_> = effects
342 .input_consensus_objects()
343 .into_iter()
344 .map(|iso| {
345 let (id, version) = iso.id_and_version();
346 let initial_version = initial_version_map
347 .get(&id)
348 .expect("transaction must have all inputs from effects");
349 ((id, *initial_version), version)
350 })
351 .collect();
352 let tx_key = cert.key();
353 trace!(
354 ?tx_key,
355 ?cert_assigned_versions,
356 "assigned consensus object versions from effects"
357 );
358 assigned_versions.push((
359 tx_key,
360 AssignedVersions::new(cert_assigned_versions, *accumulator_version),
361 ));
362 }
363 AssignedTxAndVersions::new(assigned_versions)
364 }
365
366 pub fn assign_versions_for_certificate(
367 epoch_store: &AuthorityPerEpochStore,
368 assignable: &Schedulable<impl AsTx>,
369 shared_input_next_versions: &mut HashMap<ConsensusObjectSequenceKey, SequenceNumber>,
370 cancelled_txns: &BTreeMap<TransactionDigest, CancelConsensusCertificateReason>,
371 ) -> AssignedVersions {
372 let shared_input_objects: Vec<_> = assignable.shared_input_objects(epoch_store).collect();
373
374 let accumulator_version = if epoch_store.accumulators_enabled() {
375 let accumulator_initial_version = epoch_store
376 .epoch_start_config()
377 .accumulator_root_obj_initial_shared_version()
378 .expect("accumulator root obj initial shared version should be set when accumulators are enabled");
379
380 let accumulator_version = *shared_input_next_versions
381 .get(&(SUI_ACCUMULATOR_ROOT_OBJECT_ID, accumulator_initial_version))
382 .expect("accumulator object must be in shared_input_next_versions when withdraws are enabled");
383
384 Some(accumulator_version)
385 } else {
386 None
387 };
388
389 if shared_input_objects.is_empty() {
390 return AssignedVersions::new(vec![], accumulator_version);
392 }
393
394 let tx_key = assignable.key();
395
396 let cancellation_info = tx_key
398 .as_digest()
399 .and_then(|tx_digest| cancelled_txns.get(tx_digest));
400 let congested_objects_info: Option<HashSet<_>> =
401 if let Some(CancelConsensusCertificateReason::CongestionOnObjects(congested_objects)) =
402 &cancellation_info
403 {
404 Some(congested_objects.iter().cloned().collect())
405 } else {
406 None
407 };
408 let txn_cancelled = cancellation_info.is_some();
409
410 let mut input_object_keys = assignable.non_shared_input_object_keys();
411 let mut assigned_versions = Vec::with_capacity(shared_input_objects.len());
412 let mut is_exclusively_accessed_input = Vec::with_capacity(shared_input_objects.len());
413 let receiving_object_keys = assignable.receiving_object_keys();
415 input_object_keys.extend(receiving_object_keys);
416
417 if txn_cancelled {
418 for SharedInputObject {
421 id,
422 initial_shared_version,
423 ..
424 } in shared_input_objects.iter()
425 {
426 let assigned_version = match cancellation_info {
427 Some(CancelConsensusCertificateReason::CongestionOnObjects(_)) => {
428 if congested_objects_info
429 .as_ref()
430 .is_some_and(|info| info.contains(id))
431 {
432 SequenceNumber::CONGESTED
433 } else {
434 SequenceNumber::CANCELLED_READ
435 }
436 }
437 Some(CancelConsensusCertificateReason::DkgFailed) => {
438 if id == &SUI_RANDOMNESS_STATE_OBJECT_ID {
439 SequenceNumber::RANDOMNESS_UNAVAILABLE
440 } else {
441 SequenceNumber::CANCELLED_READ
442 }
443 }
444 None => unreachable!("cancelled transaction should have cancellation info"),
445 };
446 assigned_versions.push(((*id, *initial_shared_version), assigned_version));
447 is_exclusively_accessed_input.push(false);
448 }
449 } else {
450 for (
451 SharedInputObject {
452 id,
453 initial_shared_version,
454 mutability,
455 },
456 assigned_version,
457 ) in shared_input_objects.iter().map(|obj| {
458 (
459 obj,
460 *shared_input_next_versions
461 .get(&obj.id_and_version())
462 .unwrap(),
463 )
464 }) {
465 assigned_versions.push(((*id, *initial_shared_version), assigned_version));
466 input_object_keys.push(ObjectKey(*id, assigned_version));
467 is_exclusively_accessed_input.push(mutability.is_exclusive());
468 }
469 }
470
471 let next_version =
472 SequenceNumber::lamport_increment(input_object_keys.iter().map(|obj| obj.1));
473 assert!(
474 next_version.is_valid(),
475 "Assigned version must be valid. Got {:?}",
476 next_version
477 );
478
479 if !txn_cancelled {
480 assigned_versions
482 .iter()
483 .zip_debug_eq(is_exclusively_accessed_input)
484 .filter_map(|((id, _), mutable)| {
485 if mutable {
486 Some((*id, next_version))
487 } else {
488 None
489 }
490 })
491 .for_each(|(id, version)| {
492 assert!(
493 version.is_valid(),
494 "Assigned version must be a valid version."
495 );
496 shared_input_next_versions
497 .insert(id, version)
498 .expect("Object must exist in shared_input_next_versions.");
499 });
500 }
501
502 trace!(
503 ?tx_key,
504 ?assigned_versions,
505 ?next_version,
506 ?txn_cancelled,
507 "locking shared objects"
508 );
509
510 AssignedVersions::new(assigned_versions, accumulator_version)
511 }
512}
513
514fn get_or_init_versions<'a>(
515 shared_input_objects: impl Iterator<Item = SharedInputObject> + 'a,
516 epoch_store: &AuthorityPerEpochStore,
517 cache_reader: &dyn ObjectCacheRead,
518) -> SuiResult<HashMap<ConsensusObjectSequenceKey, SequenceNumber>> {
519 let mut shared_input_objects: Vec<_> = shared_input_objects
520 .map(|so| so.into_id_and_version())
521 .collect();
522
523 if epoch_store.accumulators_enabled() {
524 shared_input_objects.push((
525 SUI_ACCUMULATOR_ROOT_OBJECT_ID,
526 epoch_store
527 .epoch_start_config()
528 .accumulator_root_obj_initial_shared_version()
529 .expect("accumulator root obj initial shared version should be set"),
530 ));
531 }
532
533 shared_input_objects.sort();
534 shared_input_objects.dedup();
535
536 epoch_store.get_or_init_next_object_versions(&shared_input_objects, cache_reader)
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542
543 use crate::authority::AuthorityState;
544 use crate::authority::epoch_start_configuration::EpochStartConfigTrait;
545 use crate::authority::shared_object_version_manager::{
546 ConsensusSharedObjVerAssignment, SharedObjVerManager,
547 };
548 use crate::authority::test_authority_builder::TestAuthorityBuilder;
549 use std::collections::{BTreeMap, HashMap};
550 use std::sync::Arc;
551 use sui_protocol_config::ProtocolConfig;
552 use sui_test_transaction_builder::TestTransactionBuilder;
553 use sui_types::base_types::{ObjectID, SequenceNumber, SuiAddress};
554 use sui_types::crypto::{RandomnessRound, get_account_key_pair};
555 use sui_types::digests::ObjectDigest;
556 use sui_types::effects::TestEffectsBuilder;
557 use sui_types::executable_transaction::{
558 CertificateProof, ExecutableTransaction, VerifiedExecutableTransaction,
559 };
560
561 use sui_types::object::Object;
562 use sui_types::transaction::{ObjectArg, SenderSignedData, VerifiedTransaction};
563
564 use sui_types::gas_coin::GAS;
565 use sui_types::transaction::FundsWithdrawalArg;
566 use sui_types::{SUI_ACCUMULATOR_ROOT_OBJECT_ID, SUI_RANDOMNESS_STATE_OBJECT_ID};
567
568 #[tokio::test]
569 async fn test_assign_versions_from_consensus_basic() {
570 let shared_object = Object::shared_for_testing();
571 let id = shared_object.id();
572 let init_shared_version = shared_object.owner.start_version().unwrap();
573 let authority = TestAuthorityBuilder::new()
574 .with_starting_objects(std::slice::from_ref(&shared_object))
575 .build()
576 .await;
577 let certs = [
578 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 3),
579 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, false)], 5),
580 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 9),
581 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 11),
582 ];
583 let epoch_store = authority.epoch_store_for_testing();
584 let assignables = certs
585 .iter()
586 .map(Schedulable::Transaction)
587 .collect::<Vec<_>>();
588 let ConsensusSharedObjVerAssignment {
589 shared_input_next_versions,
590 assigned_versions,
591 } = SharedObjVerManager::assign_versions_from_consensus(
592 &epoch_store,
593 authority.get_object_cache_reader().as_ref(),
594 assignables.iter(),
595 &BTreeMap::new(),
596 )
597 .unwrap();
598 assert_eq!(
600 epoch_store
601 .get_next_object_version(&id, init_shared_version)
602 .unwrap(),
603 init_shared_version
604 );
605 assert_eq!(
608 *shared_input_next_versions
609 .get(&(id, init_shared_version))
610 .unwrap(),
611 SequenceNumber::from_u64(12)
612 );
613 let expected_accumulator_version = SequenceNumber::from_u64(1);
618 assert_eq!(
619 assigned_versions.0,
620 vec![
621 (
622 certs[0].key(),
623 AssignedVersions::new(
624 vec![((id, init_shared_version), init_shared_version)],
625 Some(expected_accumulator_version)
626 )
627 ),
628 (
629 certs[1].key(),
630 AssignedVersions::new(
631 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
632 Some(expected_accumulator_version)
633 )
634 ),
635 (
636 certs[2].key(),
637 AssignedVersions::new(
638 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
639 Some(expected_accumulator_version)
640 )
641 ),
642 (
643 certs[3].key(),
644 AssignedVersions::new(
645 vec![((id, init_shared_version), SequenceNumber::from_u64(10))],
646 Some(expected_accumulator_version)
647 )
648 ),
649 ]
650 );
651 }
652
653 #[tokio::test]
654 async fn test_assign_versions_from_consensus_with_randomness() {
655 let authority = TestAuthorityBuilder::new().build().await;
656 let epoch_store = authority.epoch_store_for_testing();
657 let randomness_obj_version = epoch_store
658 .epoch_start_config()
659 .randomness_obj_initial_shared_version()
660 .unwrap();
661 let certs = [
662 VerifiedExecutableTransaction::new_system(
663 VerifiedTransaction::new_randomness_state_update(
664 epoch_store.epoch(),
665 RandomnessRound::new(1),
666 vec![],
667 randomness_obj_version,
668 ),
669 epoch_store.epoch(),
670 ),
671 generate_shared_objs_tx_with_gas_version(
672 &[(
673 SUI_RANDOMNESS_STATE_OBJECT_ID,
674 randomness_obj_version,
675 false,
677 )],
678 3,
679 ),
680 generate_shared_objs_tx_with_gas_version(
681 &[(
682 SUI_RANDOMNESS_STATE_OBJECT_ID,
683 randomness_obj_version,
684 false,
685 )],
686 5,
687 ),
688 ];
689 let assignables = certs
690 .iter()
691 .map(Schedulable::Transaction)
692 .collect::<Vec<_>>();
693 let ConsensusSharedObjVerAssignment {
694 shared_input_next_versions,
695 assigned_versions,
696 } = SharedObjVerManager::assign_versions_from_consensus(
697 &epoch_store,
698 authority.get_object_cache_reader().as_ref(),
699 assignables.iter(),
700 &BTreeMap::new(),
701 )
702 .unwrap();
703 assert_eq!(
705 epoch_store
706 .get_next_object_version(&SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version)
707 .unwrap(),
708 randomness_obj_version
709 );
710 let next_randomness_obj_version = randomness_obj_version.next();
711 assert_eq!(
712 *shared_input_next_versions
713 .get(&(SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version))
714 .unwrap(),
715 next_randomness_obj_version
717 );
718 let expected_accumulator_version = SequenceNumber::from_u64(1);
719 assert_eq!(
720 assigned_versions.0,
721 vec![
722 (
723 certs[0].key(),
724 AssignedVersions::new(
725 vec![(
726 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
727 randomness_obj_version
728 )],
729 Some(expected_accumulator_version)
730 )
731 ),
732 (
733 certs[1].key(),
734 AssignedVersions::new(
736 vec![(
737 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
738 next_randomness_obj_version
739 )],
740 Some(expected_accumulator_version)
741 )
742 ),
743 (
744 certs[2].key(),
745 AssignedVersions::new(
747 vec![(
748 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
749 next_randomness_obj_version
750 )],
751 Some(expected_accumulator_version)
752 )
753 ),
754 ]
755 );
756 }
757
758 #[tokio::test]
760 async fn test_assign_versions_from_consensus_with_cancellation() {
761 let shared_object_1 = Object::shared_for_testing();
762 let shared_object_2 = Object::shared_for_testing();
763 let id1 = shared_object_1.id();
764 let id2 = shared_object_2.id();
765 let init_shared_version_1 = shared_object_1.owner.start_version().unwrap();
766 let init_shared_version_2 = shared_object_2.owner.start_version().unwrap();
767 let authority = TestAuthorityBuilder::new()
768 .with_starting_objects(&[shared_object_1.clone(), shared_object_2.clone()])
769 .build()
770 .await;
771 let randomness_obj_version = authority
772 .epoch_store_for_testing()
773 .epoch_start_config()
774 .randomness_obj_initial_shared_version()
775 .unwrap();
776
777 let certs = [
792 generate_shared_objs_tx_with_gas_version(
793 &[
794 (id1, init_shared_version_1, true),
795 (id2, init_shared_version_2, true),
796 ],
797 3,
798 ),
799 generate_shared_objs_tx_with_gas_version(
800 &[
801 (id1, init_shared_version_1, true),
802 (id2, init_shared_version_2, true),
803 ],
804 5,
805 ),
806 generate_shared_objs_tx_with_gas_version(&[(id1, init_shared_version_1, true)], 1),
807 generate_shared_objs_tx_with_gas_version(
808 &[
809 (id1, init_shared_version_1, true),
810 (id2, init_shared_version_2, true),
811 ],
812 9,
813 ),
814 generate_shared_objs_tx_with_gas_version(
815 &[
816 (
817 SUI_RANDOMNESS_STATE_OBJECT_ID,
818 randomness_obj_version,
819 false,
820 ),
821 (id2, init_shared_version_2, true),
822 ],
823 11,
824 ),
825 ];
826 let epoch_store = authority.epoch_store_for_testing();
827
828 let cancelled_txns: BTreeMap<TransactionDigest, CancelConsensusCertificateReason> = [
830 (
831 *certs[1].digest(),
832 CancelConsensusCertificateReason::CongestionOnObjects(vec![id1]),
833 ),
834 (
835 *certs[3].digest(),
836 CancelConsensusCertificateReason::CongestionOnObjects(vec![id2]),
837 ),
838 (
839 *certs[4].digest(),
840 CancelConsensusCertificateReason::DkgFailed,
841 ),
842 ]
843 .into_iter()
844 .collect();
845
846 let assignables = certs
847 .iter()
848 .map(Schedulable::Transaction)
849 .collect::<Vec<_>>();
850
851 let ConsensusSharedObjVerAssignment {
853 mut shared_input_next_versions,
854 assigned_versions,
855 } = SharedObjVerManager::assign_versions_from_consensus(
856 &epoch_store,
857 authority.get_object_cache_reader().as_ref(),
858 assignables.iter(),
859 &cancelled_txns,
860 )
861 .unwrap();
862
863 shared_input_next_versions
866 .remove(&(SUI_ACCUMULATOR_ROOT_OBJECT_ID, SequenceNumber::from_u64(1)));
867 assert_eq!(
868 shared_input_next_versions,
869 HashMap::from([
870 ((id1, init_shared_version_1), SequenceNumber::from_u64(5)), ((id2, init_shared_version_2), SequenceNumber::from_u64(4)), (
873 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
874 SequenceNumber::from_u64(1)
875 ), ])
877 );
878
879 let expected_accumulator_version = SequenceNumber::from_u64(1);
881 assert_eq!(
882 assigned_versions.0,
883 vec![
884 (
885 certs[0].key(),
886 AssignedVersions::new(
887 vec![
888 ((id1, init_shared_version_1), init_shared_version_1),
889 ((id2, init_shared_version_2), init_shared_version_2)
890 ],
891 Some(expected_accumulator_version)
892 )
893 ),
894 (
895 certs[1].key(),
896 AssignedVersions::new(
897 vec![
898 ((id1, init_shared_version_1), SequenceNumber::CONGESTED),
899 ((id2, init_shared_version_2), SequenceNumber::CANCELLED_READ),
900 ],
901 Some(expected_accumulator_version)
902 )
903 ),
904 (
905 certs[2].key(),
906 AssignedVersions::new(
907 vec![((id1, init_shared_version_1), SequenceNumber::from_u64(4))],
908 Some(expected_accumulator_version)
909 )
910 ),
911 (
912 certs[3].key(),
913 AssignedVersions::new(
914 vec![
915 ((id1, init_shared_version_1), SequenceNumber::CANCELLED_READ),
916 ((id2, init_shared_version_2), SequenceNumber::CONGESTED)
917 ],
918 Some(expected_accumulator_version)
919 )
920 ),
921 (
922 certs[4].key(),
923 AssignedVersions::new(
924 vec![
925 (
926 (SUI_RANDOMNESS_STATE_OBJECT_ID, randomness_obj_version),
927 SequenceNumber::RANDOMNESS_UNAVAILABLE
928 ),
929 ((id2, init_shared_version_2), SequenceNumber::CANCELLED_READ)
930 ],
931 Some(expected_accumulator_version)
932 )
933 ),
934 ]
935 );
936 }
937
938 #[tokio::test]
939 async fn test_assign_versions_from_effects() {
940 let shared_object = Object::shared_for_testing();
941 let id = shared_object.id();
942 let init_shared_version = shared_object.owner.start_version().unwrap();
943 let authority = TestAuthorityBuilder::new()
944 .with_starting_objects(std::slice::from_ref(&shared_object))
945 .build()
946 .await;
947 let certs = [
948 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 3),
949 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, false)], 5),
950 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 9),
951 generate_shared_objs_tx_with_gas_version(&[(id, init_shared_version, true)], 11),
952 ];
953 let effects = [
954 TestEffectsBuilder::new(certs[0].data()).build(),
955 TestEffectsBuilder::new(certs[1].data())
956 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(4))]))
957 .build(),
958 TestEffectsBuilder::new(certs[2].data())
959 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(4))]))
960 .build(),
961 TestEffectsBuilder::new(certs[3].data())
962 .with_shared_input_versions(BTreeMap::from([(id, SequenceNumber::from_u64(10))]))
963 .build(),
964 ];
965 let epoch_store = authority.epoch_store_for_testing();
966 let assigned_versions = SharedObjVerManager::assign_versions_from_effects(
967 certs
968 .iter()
969 .zip_debug_eq(effects.iter())
970 .map(|(cert, effect)| (cert, effect, None))
971 .collect::<Vec<_>>()
972 .as_slice(),
973 &epoch_store,
974 authority.get_object_cache_reader().as_ref(),
975 );
976 assert_eq!(
978 epoch_store
979 .get_next_object_version(&id, init_shared_version)
980 .unwrap(),
981 init_shared_version
982 );
983 assert_eq!(
984 assigned_versions.0,
985 vec![
986 (
987 certs[0].key(),
988 AssignedVersions::new(
989 vec![((id, init_shared_version), init_shared_version)],
990 None
991 )
992 ),
993 (
994 certs[1].key(),
995 AssignedVersions::new(
996 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
997 None
998 )
999 ),
1000 (
1001 certs[2].key(),
1002 AssignedVersions::new(
1003 vec![((id, init_shared_version), SequenceNumber::from_u64(4))],
1004 None
1005 )
1006 ),
1007 (
1008 certs[3].key(),
1009 AssignedVersions::new(
1010 vec![((id, init_shared_version), SequenceNumber::from_u64(10))],
1011 None
1012 )
1013 ),
1014 ]
1015 );
1016 }
1017
1018 fn generate_shared_objs_tx_with_gas_version(
1022 shared_objects: &[(ObjectID, SequenceNumber, bool)],
1023 gas_object_version: u64,
1024 ) -> VerifiedExecutableTransaction {
1025 let mut tx_builder = TestTransactionBuilder::new(
1026 SuiAddress::ZERO,
1027 (
1028 ObjectID::random(),
1029 SequenceNumber::from_u64(gas_object_version),
1030 ObjectDigest::random(),
1031 ),
1032 0,
1033 );
1034 let tx_data = {
1035 let builder = tx_builder.ptb_builder_mut();
1036 for (shared_object_id, shared_object_init_version, shared_object_mutable) in
1037 shared_objects
1038 {
1039 builder
1040 .obj(ObjectArg::SharedObject {
1041 id: *shared_object_id,
1042 initial_shared_version: *shared_object_init_version,
1043 mutability: if *shared_object_mutable {
1044 SharedObjectMutability::Mutable
1045 } else {
1046 SharedObjectMutability::Immutable
1047 },
1048 })
1049 .unwrap();
1050 }
1051 tx_builder.build()
1052 };
1053 let tx = SenderSignedData::new(tx_data, vec![]);
1054 VerifiedExecutableTransaction::new_unchecked(ExecutableTransaction::new_from_data_and_sig(
1055 tx,
1056 CertificateProof::new_system(0),
1057 ))
1058 }
1059
1060 struct WithdrawTestContext {
1061 authority: Arc<AuthorityState>,
1062 assignables: Vec<Schedulable<VerifiedExecutableTransaction>>,
1063 shared_objects: Vec<Object>,
1064 }
1065
1066 impl WithdrawTestContext {
1067 pub async fn new() -> Self {
1068 let shared_objects = vec![Object::shared_for_testing()];
1070 let mut config = ProtocolConfig::get_for_max_version_UNSAFE();
1071 config.enable_accumulators_for_testing();
1072 let authority = TestAuthorityBuilder::new()
1073 .with_starting_objects(&shared_objects)
1074 .with_protocol_config(config)
1075 .build()
1076 .await;
1077 Self {
1078 authority,
1079 assignables: vec![],
1080 shared_objects,
1081 }
1082 }
1083
1084 pub fn add_withdraw_transaction(&mut self) -> TransactionKey {
1085 let (sender, keypair) = get_account_key_pair();
1087 let gas_object = Object::with_owner_for_testing(sender);
1088 let gas_object_ref = gas_object.compute_object_reference();
1089 let gas_price = (self.assignables.len() + 1) as u64;
1091 let mut tx_builder = TestTransactionBuilder::new(sender, gas_object_ref, gas_price);
1092 let tx_data = {
1093 let ptb_builder = tx_builder.ptb_builder_mut();
1094 ptb_builder
1095 .funds_withdrawal(FundsWithdrawalArg::balance_from_sender(
1096 200,
1097 GAS::type_tag(),
1098 ))
1099 .unwrap();
1100 tx_builder.build()
1101 };
1102 let cert = VerifiedExecutableTransaction::new_for_testing(tx_data, &keypair);
1103 let key = cert.key();
1104 self.assignables.push(Schedulable::Transaction(cert));
1105 key
1106 }
1107
1108 pub fn add_settlement_transaction(&mut self) -> TransactionKey {
1109 let height = (self.assignables.len() + 1) as u64;
1110 let settlement = Schedulable::AccumulatorSettlement(0, height);
1111 let key = settlement.key();
1112 self.assignables.push(settlement);
1113 key
1114 }
1115
1116 pub fn add_withdraw_with_shared_object_transaction(&mut self) -> TransactionKey {
1117 let (sender, keypair) = get_account_key_pair();
1119 let gas_object = Object::with_owner_for_testing(sender);
1120 let gas_object_ref = gas_object.compute_object_reference();
1121 let gas_price = (self.assignables.len() + 1) as u64;
1123 let mut tx_builder = TestTransactionBuilder::new(sender, gas_object_ref, gas_price);
1124 let tx_data = {
1125 let ptb_builder = tx_builder.ptb_builder_mut();
1126 if let Some(shared_obj) = self.shared_objects.first() {
1128 let id = shared_obj.id();
1129 let init_version = shared_obj.owner.start_version().unwrap();
1130 ptb_builder
1131 .obj(ObjectArg::SharedObject {
1132 id,
1133 initial_shared_version: init_version,
1134 mutability: SharedObjectMutability::Mutable,
1135 })
1136 .unwrap();
1137 }
1138 ptb_builder
1140 .funds_withdrawal(FundsWithdrawalArg::balance_from_sender(
1141 200,
1142 GAS::type_tag(),
1143 ))
1144 .unwrap();
1145 tx_builder.build()
1146 };
1147 let cert = VerifiedExecutableTransaction::new_for_testing(tx_data, &keypair);
1148 let key = cert.key();
1149 self.assignables.push(Schedulable::Transaction(cert));
1150 key
1151 }
1152
1153 pub fn assign_versions_from_consensus(&self) -> ConsensusSharedObjVerAssignment {
1154 let epoch_store = self.authority.epoch_store_for_testing();
1155 SharedObjVerManager::assign_versions_from_consensus(
1156 &epoch_store,
1157 self.authority.get_object_cache_reader().as_ref(),
1158 self.assignables.iter(),
1159 &BTreeMap::new(),
1160 )
1161 .unwrap()
1162 }
1163 }
1164
1165 #[tokio::test]
1166 async fn test_assign_versions_from_consensus_with_withdraws_simple() {
1167 let mut ctx = WithdrawTestContext::new().await;
1170
1171 let acc_version = ctx
1172 .authority
1173 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1174 .unwrap()
1175 .version();
1176
1177 let withdraw_key = ctx.add_withdraw_transaction();
1178 let settlement_key = ctx.add_settlement_transaction();
1179
1180 let assigned_versions = ctx.assign_versions_from_consensus();
1181 assert_eq!(
1182 assigned_versions,
1183 ConsensusSharedObjVerAssignment {
1184 assigned_versions: AssignedTxAndVersions::new(vec![
1185 (
1186 withdraw_key,
1187 AssignedVersions {
1188 shared_object_versions: vec![],
1189 accumulator_version: Some(acc_version),
1190 }
1191 ),
1192 (
1193 settlement_key,
1194 AssignedVersions {
1195 shared_object_versions: vec![(
1196 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1197 acc_version
1198 )],
1199 accumulator_version: Some(acc_version),
1200 }
1201 ),
1202 ]),
1203 shared_input_next_versions: HashMap::from([(
1204 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1205 acc_version.next()
1206 )]),
1207 }
1208 );
1209 }
1210
1211 #[tokio::test]
1212 async fn test_assign_versions_from_consensus_with_multiple_withdraws_and_settlements() {
1213 let mut ctx = WithdrawTestContext::new().await;
1215
1216 let acc_version = ctx
1217 .authority
1218 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1219 .unwrap()
1220 .version();
1221
1222 let withdraw_key1 = ctx.add_withdraw_transaction();
1224 let settlement_key1 = ctx.add_settlement_transaction();
1225
1226 let withdraw_key2 = ctx.add_withdraw_transaction();
1228 let settlement_key2 = ctx.add_settlement_transaction();
1229
1230 let withdraw_key3 = ctx.add_withdraw_transaction();
1232 let settlement_key3 = ctx.add_settlement_transaction();
1233
1234 let assigned_versions = ctx.assign_versions_from_consensus();
1235 assert_eq!(
1236 assigned_versions,
1237 ConsensusSharedObjVerAssignment {
1238 assigned_versions: AssignedTxAndVersions::new(vec![
1239 (
1240 withdraw_key1,
1241 AssignedVersions {
1242 shared_object_versions: vec![],
1243 accumulator_version: Some(acc_version),
1244 }
1245 ),
1246 (
1247 settlement_key1,
1248 AssignedVersions {
1249 shared_object_versions: vec![(
1250 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1251 acc_version
1252 )],
1253 accumulator_version: Some(acc_version),
1254 }
1255 ),
1256 (
1257 withdraw_key2,
1258 AssignedVersions {
1259 shared_object_versions: vec![],
1260 accumulator_version: Some(acc_version.next()),
1261 }
1262 ),
1263 (
1264 settlement_key2,
1265 AssignedVersions {
1266 shared_object_versions: vec![(
1267 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1268 acc_version.next()
1269 )],
1270 accumulator_version: Some(acc_version.next()),
1271 }
1272 ),
1273 (
1274 withdraw_key3,
1275 AssignedVersions {
1276 shared_object_versions: vec![],
1277 accumulator_version: Some(acc_version.next().next()),
1278 }
1279 ),
1280 (
1281 settlement_key3,
1282 AssignedVersions {
1283 shared_object_versions: vec![(
1284 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1285 acc_version.next().next()
1286 )],
1287 accumulator_version: Some(acc_version.next().next()),
1288 }
1289 ),
1290 ]),
1291 shared_input_next_versions: HashMap::from([(
1292 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1293 acc_version.next().next().next()
1294 )]),
1295 }
1296 );
1297 }
1298
1299 #[tokio::test]
1300 async fn test_assign_versions_from_consensus_with_withdraw_and_shared_object() {
1301 let mut ctx = WithdrawTestContext::new().await;
1303
1304 let shared_obj_id = ctx.shared_objects[0].id();
1306 let shared_obj_version = ctx.shared_objects[0].owner.start_version().unwrap();
1307
1308 let acc_version = ctx
1309 .authority
1310 .get_object(&SUI_ACCUMULATOR_ROOT_OBJECT_ID)
1311 .unwrap()
1312 .version();
1313
1314 let withdraw_with_shared_key = ctx.add_withdraw_with_shared_object_transaction();
1315 let settlement_key = ctx.add_settlement_transaction();
1316
1317 let assigned_versions = ctx.assign_versions_from_consensus();
1318 assert_eq!(
1319 assigned_versions,
1320 ConsensusSharedObjVerAssignment {
1321 assigned_versions: AssignedTxAndVersions::new(vec![
1322 (
1323 withdraw_with_shared_key,
1324 AssignedVersions {
1325 shared_object_versions: vec![(
1326 (shared_obj_id, shared_obj_version),
1327 shared_obj_version
1328 )],
1329 accumulator_version: Some(acc_version),
1330 }
1331 ),
1332 (
1333 settlement_key,
1334 AssignedVersions {
1335 shared_object_versions: vec![(
1336 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1337 acc_version
1338 )],
1339 accumulator_version: Some(acc_version),
1340 }
1341 ),
1342 ]),
1343 shared_input_next_versions: HashMap::from([
1344 (
1345 (SUI_ACCUMULATOR_ROOT_OBJECT_ID, acc_version),
1346 acc_version.next()
1347 ),
1348 (
1349 (shared_obj_id, shared_obj_version),
1350 shared_obj_version.next()
1351 ),
1352 ]),
1353 }
1354 );
1355 }
1356}