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