sui_single_node_benchmark/
single_node.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::command::Component;
5use crate::mock_storage::InMemoryObjectStore;
6use std::collections::{BTreeMap, HashMap};
7use std::sync::Arc;
8use sui_core::authority::authority_per_epoch_store::AuthorityPerEpochStore;
9use sui_core::authority::authority_store_tables::LiveObject;
10use sui_core::authority::shared_object_version_manager::{
11    AssignedTxAndVersions, AssignedVersions, Schedulable,
12};
13use sui_core::authority::test_authority_builder::TestAuthorityBuilder;
14use sui_core::authority::{AuthorityState, ExecutionEnv};
15use sui_core::authority_server::{ValidatorService, ValidatorServiceMetrics};
16use sui_core::checkpoints::checkpoint_executor::CheckpointExecutor;
17use sui_core::consensus_adapter::{ConsensusAdapter, ConsensusAdapterMetrics};
18use sui_core::global_state_hasher::GlobalStateHasher;
19use sui_core::mock_checkpoint_builder::{MockCheckpointBuilder, ValidatorKeypairProvider};
20use sui_core::mock_consensus::{ConsensusMode, MockConsensusClient};
21use sui_test_transaction_builder::{PublishData, TestTransactionBuilder};
22use sui_types::base_types::{AuthorityName, ObjectRef, SuiAddress, TransactionDigest};
23use sui_types::committee::Committee;
24use sui_types::crypto::{AccountKeyPair, AuthoritySignature, Signer};
25use sui_types::effects::{TransactionEffects, TransactionEffectsAPI};
26use sui_types::executable_transaction::VerifiedExecutableTransaction;
27use sui_types::execution_params::ExecutionOrEarlyError;
28use sui_types::messages_checkpoint::{VerifiedCheckpoint, VerifiedCheckpointContents};
29use sui_types::object::Object;
30use sui_types::transaction::{
31    DEFAULT_VALIDATOR_GAS_PRICE, Transaction, TransactionDataAPI, VerifiedTransaction,
32};
33
34#[derive(Clone)]
35pub struct SingleValidator {
36    validator_service: Arc<ValidatorService>,
37    epoch_store: Arc<AuthorityPerEpochStore>,
38}
39
40impl SingleValidator {
41    pub(crate) async fn new(genesis_objects: &[Object], component: Component) -> Self {
42        let validator = TestAuthorityBuilder::new()
43            .disable_indexer()
44            .with_starting_objects(genesis_objects)
45            // This is needed to properly run checkpoint executor.
46            .insert_genesis_checkpoint()
47            .build()
48            .await;
49        let epoch_store = validator.epoch_store_for_testing().clone();
50        let consensus_mode = match component {
51            Component::ValidatorWithFakeConsensus => ConsensusMode::DirectSequencing,
52            _ => ConsensusMode::Noop,
53        };
54        let consensus_adapter = Arc::new(ConsensusAdapter::new(
55            Arc::new(MockConsensusClient::new(
56                Arc::downgrade(&validator),
57                consensus_mode,
58            )),
59            validator.checkpoint_store.clone(),
60            validator.name,
61            100_000,
62            100_000,
63            ConsensusAdapterMetrics::new_test(),
64            Arc::new(tokio::sync::Notify::new()),
65        ));
66        // TODO: for validator benchmarking purposes, we should allow for traffic control
67        // to be configurable and introduce traffic control benchmarks to test
68        // against different policies
69        let validator_service = Arc::new(ValidatorService::new_for_tests(
70            validator,
71            consensus_adapter,
72            Arc::new(ValidatorServiceMetrics::new_for_tests()),
73        ));
74        Self {
75            validator_service,
76            epoch_store,
77        }
78    }
79
80    pub fn get_validator(&self) -> &Arc<AuthorityState> {
81        self.validator_service.validator_state()
82    }
83
84    pub fn get_epoch(&self) -> u64 {
85        self.epoch_store.epoch()
86    }
87
88    /// Publish a package, returns the package object and the updated gas object.
89    pub async fn publish_package(
90        &self,
91        publish_data: PublishData,
92        sender: SuiAddress,
93        keypair: &AccountKeyPair,
94        gas: ObjectRef,
95    ) -> (ObjectRef, ObjectRef) {
96        let tx_builder = TestTransactionBuilder::new(sender, gas, DEFAULT_VALIDATOR_GAS_PRICE)
97            .publish_with_data_async(publish_data)
98            .await;
99        let transaction = tx_builder.build_and_sign(keypair);
100        let effects = self.execute_raw_transaction(transaction).await;
101        let package = effects
102            .all_changed_objects()
103            .into_iter()
104            .filter_map(|(oref, owner, _)| owner.is_immutable().then_some(oref))
105            .next()
106            .unwrap();
107        let updated_gas = effects.gas_object().unwrap().0;
108        (package, updated_gas)
109    }
110
111    pub async fn execute_raw_transaction(&self, transaction: Transaction) -> TransactionEffects {
112        let executable = VerifiedExecutableTransaction::new_from_consensus(
113            VerifiedTransaction::new_unchecked(transaction),
114            0,
115        );
116        let effects = self
117            .get_validator()
118            .try_execute_immediately(&executable, ExecutionEnv::new(), &self.epoch_store)
119            .await
120            .unwrap()
121            .0;
122        assert!(effects.status().is_ok());
123        effects
124    }
125
126    pub async fn execute_dry_run(&self, transaction: Transaction) -> TransactionEffects {
127        let effects = self
128            .get_validator()
129            .dry_exec_transaction_for_benchmark(
130                transaction.data().intent_message().value.clone(),
131                *transaction.digest(),
132            )
133            .unwrap()
134            .2;
135        assert!(effects.status().is_ok());
136        effects
137    }
138
139    /// Creates a VerifiedExecutableTransaction from a Transaction using MFP style certification.
140    fn create_executable(&self, transaction: Transaction) -> VerifiedExecutableTransaction {
141        VerifiedExecutableTransaction::new_from_consensus(
142            VerifiedTransaction::new_unchecked(transaction),
143            self.epoch_store.epoch(),
144        )
145    }
146
147    pub async fn execute_transaction(
148        &self,
149        transaction: Transaction,
150        assigned_versions: &AssignedVersions,
151        component: Component,
152    ) -> (TransactionEffects, std::time::Duration) {
153        let executable = self.create_executable(transaction);
154        let start = std::time::Instant::now();
155        let effects = match component {
156            Component::Baseline => {
157                self.get_validator()
158                    .try_execute_immediately(
159                        &executable,
160                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
161                        &self.epoch_store,
162                    )
163                    .await
164                    .unwrap()
165                    .0
166            }
167            Component::WithTxManager => {
168                // Manually enqueue since we don't have consensus.
169                self.get_validator().execution_scheduler().enqueue(
170                    vec![(
171                        executable.clone().into(),
172                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
173                    )],
174                    &self.epoch_store,
175                );
176                self.get_validator()
177                    .wait_for_transaction_execution_for_testing(&executable)
178                    .await
179            }
180            Component::ValidatorWithoutConsensus | Component::ValidatorWithFakeConsensus => {
181                // Execute the transaction directly using try_execute_executable_for_test
182                let (signed_effects, _) = self
183                    .get_validator()
184                    .try_execute_executable_for_test(
185                        &executable,
186                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
187                    )
188                    .await;
189                signed_effects.into_inner().into_data()
190            }
191            Component::CheckpointExecutor | Component::ExecutionOnly => {
192                unreachable!()
193            }
194        };
195        let elapsed = start.elapsed();
196        assert!(effects.status().is_ok());
197        (effects, elapsed)
198    }
199
200    pub(crate) async fn execute_transaction_in_memory(
201        &self,
202        store: InMemoryObjectStore,
203        transaction: Transaction,
204        assigned_versions: &AssignedVersions,
205    ) -> TransactionEffects {
206        let input_objects = transaction
207            .data()
208            .intent_message()
209            .value
210            .input_objects()
211            .unwrap();
212        let executable = self.create_executable(transaction);
213        let objects = store
214            .read_objects_for_execution(&executable.key(), assigned_versions, &input_objects)
215            .unwrap();
216
217        let (gas_status, input_objects) = sui_transaction_checks::check_certificate_input(
218            &executable,
219            objects,
220            self.epoch_store.protocol_config(),
221            self.epoch_store.reference_gas_price(),
222        )
223        .unwrap();
224        let (kind, signer, gas_data) = executable.transaction_data().execution_parts();
225        let (inner_temp_store, _, effects, _timings, _) =
226            self.epoch_store.executor().execute_transaction_to_effects(
227                &store,
228                self.epoch_store.protocol_config(),
229                self.get_validator().metrics.execution_metrics.clone(),
230                false,
231                ExecutionOrEarlyError::Ok(()),
232                &self.epoch_store.epoch(),
233                0,
234                input_objects,
235                gas_data,
236                gas_status,
237                kind,
238                None, // compat_args
239                signer,
240                *executable.digest(),
241                &mut None,
242            );
243        assert!(effects.status().is_ok());
244        store.commit_objects(inner_temp_store);
245        effects
246    }
247
248    pub(crate) async fn build_checkpoints(
249        &self,
250        transactions: Vec<Transaction>,
251        mut all_effects: BTreeMap<TransactionDigest, TransactionEffects>,
252        checkpoint_size: usize,
253    ) -> Vec<(VerifiedCheckpoint, VerifiedCheckpointContents)> {
254        let mut builder = MockCheckpointBuilder::new(
255            self.get_validator()
256                .get_checkpoint_store()
257                .get_latest_certified_checkpoint()
258                .unwrap()
259                .unwrap(),
260        );
261        let mut checkpoints = vec![];
262        for transaction in transactions {
263            let effects = all_effects.remove(transaction.digest()).unwrap();
264            builder.push_transaction(VerifiedTransaction::new_unchecked(transaction), effects);
265            if builder.size() == checkpoint_size {
266                let (checkpoint, _, full_contents) = builder.build(self, 0);
267                checkpoints.push((checkpoint, full_contents));
268            }
269        }
270        if builder.size() > 0 {
271            let (checkpoint, _, full_contents) = builder.build(self, 0);
272            checkpoints.push((checkpoint, full_contents));
273        }
274        checkpoints
275    }
276
277    pub fn create_checkpoint_executor(&self) -> CheckpointExecutor {
278        let validator = self.get_validator();
279        CheckpointExecutor::new_for_tests(
280            self.epoch_store.clone(),
281            validator.get_checkpoint_store().clone(),
282            validator.clone(),
283            Arc::new(GlobalStateHasher::new_for_tests(
284                validator.get_global_state_hash_store().clone(),
285            )),
286        )
287    }
288
289    pub(crate) fn create_in_memory_store(&self) -> InMemoryObjectStore {
290        let objects: HashMap<_, _> = self
291            .get_validator()
292            .get_global_state_hash_store()
293            .iter_cached_live_object_set_for_testing(false)
294            .map(|o| match o {
295                LiveObject::Normal(object) => (object.id(), object),
296                LiveObject::Wrapped(_) => unreachable!(),
297            })
298            .collect();
299        InMemoryObjectStore::new(objects)
300    }
301
302    pub(crate) async fn assigned_shared_object_versions(
303        &self,
304        transactions: &[Transaction],
305    ) -> AssignedTxAndVersions {
306        let executables: Vec<_> = transactions
307            .iter()
308            .map(|tx| self.create_executable(tx.clone()))
309            .collect();
310        let assignables: Vec<_> = executables.iter().map(Schedulable::Transaction).collect();
311        self.epoch_store
312            .assign_shared_object_versions_idempotent(
313                self.get_validator().get_object_cache_reader().as_ref(),
314                assignables.iter(),
315            )
316            .unwrap()
317    }
318}
319
320impl ValidatorKeypairProvider for SingleValidator {
321    fn get_validator_key(&self, name: &AuthorityName) -> &dyn Signer<AuthoritySignature> {
322        assert_eq!(name, &self.get_validator().name);
323        &*self.get_validator().secret
324    }
325
326    fn get_committee(&self) -> &Committee {
327        self.epoch_store.committee().as_ref()
328    }
329}