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