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