sui_core/
mock_checkpoint_builder.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use fastcrypto::traits::Signer;
5use std::mem;
6use sui_protocol_config::ProtocolConfig;
7use sui_types::base_types::{AuthorityName, VerifiedExecutionData};
8use sui_types::committee::Committee;
9use sui_types::crypto::{AuthoritySignInfo, AuthoritySignature, SuiAuthoritySignature};
10use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
11use sui_types::gas::GasCostSummary;
12use sui_types::messages_checkpoint::{
13    CertifiedCheckpointSummary, CheckpointContents, CheckpointSummary,
14    CheckpointVersionSpecificData, EndOfEpochData, FullCheckpointContents, VerifiedCheckpoint,
15    VerifiedCheckpointContents, VersionedFullCheckpointContents,
16};
17use sui_types::object::OBJECT_START_VERSION;
18use sui_types::transaction::{Transaction, VerifiedTransaction};
19
20use crate::accumulators::AccumulatorSettlementTxBuilder;
21
22pub trait ValidatorKeypairProvider {
23    fn get_validator_key(&self, name: &AuthorityName) -> &dyn Signer<AuthoritySignature>;
24    fn get_committee(&self) -> &Committee;
25}
26
27/// A utility to build consecutive checkpoints by adding transactions to the checkpoint builder.
28/// It's mostly used by simulations, tests and benchmarks.
29#[derive(Debug)]
30pub struct MockCheckpointBuilder {
31    previous_checkpoint: Option<VerifiedCheckpoint>,
32    transactions: Vec<VerifiedExecutionData>,
33    epoch_rolling_gas_cost_summary: GasCostSummary,
34    epoch: u64,
35}
36
37impl MockCheckpointBuilder {
38    pub fn new(previous_checkpoint: VerifiedCheckpoint) -> Self {
39        let epoch_rolling_gas_cost_summary =
40            previous_checkpoint.epoch_rolling_gas_cost_summary.clone();
41        let epoch = previous_checkpoint.epoch;
42
43        Self {
44            previous_checkpoint: Some(previous_checkpoint),
45            transactions: Vec::new(),
46            epoch_rolling_gas_cost_summary,
47            epoch,
48        }
49    }
50
51    pub fn size(&self) -> usize {
52        self.transactions.len()
53    }
54
55    pub fn epoch_rolling_gas_cost_summary(&self) -> &GasCostSummary {
56        &self.epoch_rolling_gas_cost_summary
57    }
58
59    pub fn push_transaction(
60        &mut self,
61        transaction: VerifiedTransaction,
62        effects: TransactionEffects,
63    ) {
64        self.epoch_rolling_gas_cost_summary += effects.gas_cost_summary();
65
66        self.transactions
67            .push(VerifiedExecutionData::new(transaction, effects))
68    }
69
70    pub fn get_next_checkpoint_number(&self) -> u64 {
71        self.previous_checkpoint
72            .as_ref()
73            .map(|c| c.sequence_number + 1)
74            .unwrap_or_default()
75    }
76
77    /// Override the next checkpoint number to generate.
78    /// This can be useful to generate checkpoints with specific sequence numbers.
79    pub fn override_next_checkpoint_number(
80        &mut self,
81        checkpoint_number: u64,
82        validator_keys: &impl ValidatorKeypairProvider,
83    ) {
84        if checkpoint_number > 0 {
85            let mut summary = self.previous_checkpoint.as_ref().unwrap().data().clone();
86            summary.sequence_number = checkpoint_number - 1;
87            let checkpoint = Self::create_certified_checkpoint(validator_keys, summary);
88            self.previous_checkpoint = Some(checkpoint);
89        } else {
90            self.previous_checkpoint = None;
91        }
92    }
93
94    /// Builds a checkpoint using internally buffered transactions.
95    pub fn build(
96        &mut self,
97        validator_keys: &impl ValidatorKeypairProvider,
98        timestamp_ms: u64,
99    ) -> (
100        VerifiedCheckpoint,
101        CheckpointContents,
102        VerifiedCheckpointContents,
103    ) {
104        self.build_internal(validator_keys, timestamp_ms, None)
105    }
106
107    pub fn build_end_of_epoch(
108        &mut self,
109        validator_keys: &impl ValidatorKeypairProvider,
110        timestamp_ms: u64,
111        new_epoch: u64,
112        end_of_epoch_data: EndOfEpochData,
113    ) -> (
114        VerifiedCheckpoint,
115        CheckpointContents,
116        VerifiedCheckpointContents,
117    ) {
118        self.build_internal(
119            validator_keys,
120            timestamp_ms,
121            Some((new_epoch, end_of_epoch_data)),
122        )
123    }
124
125    // Returns the settlement transactions and associated barrier transaction (in the last position).
126    // The barrier transaction must be executed after all the settlement transactions.
127    pub fn get_settlement_txns(&self, protocol_config: &ProtocolConfig) -> Vec<Transaction> {
128        let effects: Vec<_> = self
129            .transactions
130            .iter()
131            .map(|e| e.effects.clone())
132            .collect();
133
134        let checkpoint_height = self.get_next_checkpoint_number();
135        let checkpoint_seq = checkpoint_height;
136
137        let builder = AccumulatorSettlementTxBuilder::new(None, &effects, checkpoint_seq, 0);
138
139        let (settlement_txns, barrier_tx) = builder.build_tx(
140            protocol_config,
141            self.epoch,
142            OBJECT_START_VERSION,
143            checkpoint_height,
144            checkpoint_seq,
145        );
146
147        settlement_txns
148            .into_iter()
149            .chain(std::iter::once(barrier_tx))
150            .map(|tx| VerifiedTransaction::new_system_transaction(tx).into_inner())
151            .collect()
152    }
153
154    fn build_internal(
155        &mut self,
156        validator_keys: &impl ValidatorKeypairProvider,
157        timestamp_ms: u64,
158        new_epoch_data: Option<(u64, EndOfEpochData)>,
159    ) -> (
160        VerifiedCheckpoint,
161        CheckpointContents,
162        VerifiedCheckpointContents,
163    ) {
164        let contents =
165            CheckpointContents::new_with_causally_ordered_execution_data(self.transactions.iter());
166        let full_contents =
167            VerifiedCheckpointContents::new_unchecked(VersionedFullCheckpointContents::V1(
168                FullCheckpointContents::new_with_causally_ordered_transactions(
169                    mem::take(&mut self.transactions)
170                        .into_iter()
171                        .map(|e| e.into_inner()),
172                ),
173            ));
174
175        let (epoch, epoch_rolling_gas_cost_summary, end_of_epoch_data) =
176            if let Some((next_epoch, end_of_epoch_data)) = new_epoch_data {
177                let epoch = std::mem::replace(&mut self.epoch, next_epoch);
178                assert_eq!(next_epoch, epoch + 1);
179                let epoch_rolling_gas_cost_summary =
180                    std::mem::take(&mut self.epoch_rolling_gas_cost_summary);
181
182                (
183                    epoch,
184                    epoch_rolling_gas_cost_summary,
185                    Some(end_of_epoch_data),
186                )
187            } else {
188                (
189                    self.epoch,
190                    self.epoch_rolling_gas_cost_summary.clone(),
191                    None,
192                )
193            };
194
195        let summary = CheckpointSummary {
196            epoch,
197            sequence_number: self
198                .previous_checkpoint
199                .as_ref()
200                .map(|c| c.sequence_number + 1)
201                .unwrap_or_default(),
202            network_total_transactions: self
203                .previous_checkpoint
204                .as_ref()
205                .map(|c| c.network_total_transactions)
206                .unwrap_or_default()
207                + contents.size() as u64,
208            content_digest: *contents.digest(),
209            previous_digest: self.previous_checkpoint.as_ref().map(|c| *c.digest()),
210            epoch_rolling_gas_cost_summary,
211            end_of_epoch_data,
212            timestamp_ms,
213            version_specific_data: bcs::to_bytes(&CheckpointVersionSpecificData::empty_for_tests())
214                .unwrap(),
215            checkpoint_commitments: Default::default(),
216        };
217
218        let checkpoint = Self::create_certified_checkpoint(validator_keys, summary);
219        self.previous_checkpoint = Some(checkpoint.clone());
220        (checkpoint, contents, full_contents)
221    }
222
223    fn create_certified_checkpoint(
224        validator_keys: &impl ValidatorKeypairProvider,
225        checkpoint: CheckpointSummary,
226    ) -> VerifiedCheckpoint {
227        let signatures = validator_keys
228            .get_committee()
229            .voting_rights
230            .iter()
231            .map(|(name, _)| {
232                let intent_msg = shared_crypto::intent::IntentMessage::new(
233                    shared_crypto::intent::Intent::sui_app(
234                        shared_crypto::intent::IntentScope::CheckpointSummary,
235                    ),
236                    &checkpoint,
237                );
238                let key = validator_keys.get_validator_key(name);
239                let signature = AuthoritySignature::new_secure(&intent_msg, &checkpoint.epoch, key);
240                AuthoritySignInfo {
241                    epoch: checkpoint.epoch,
242                    authority: *name,
243                    signature,
244                }
245            })
246            .collect();
247
248        let checkpoint_cert =
249            CertifiedCheckpointSummary::new(checkpoint, signatures, validator_keys.get_committee())
250                .unwrap();
251        VerifiedCheckpoint::new_unchecked(checkpoint_cert)
252    }
253}