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            .unwrap()
120            .0;
121        assert!(effects.status().is_ok());
122        effects
123    }
124
125    /// Creates a VerifiedExecutableTransaction from a Transaction using MFP style certification.
126    fn create_executable(&self, transaction: Transaction) -> VerifiedExecutableTransaction {
127        VerifiedExecutableTransaction::new_from_consensus(
128            VerifiedTransaction::new_unchecked(transaction),
129            self.epoch_store.epoch(),
130        )
131    }
132
133    pub async fn execute_transaction(
134        &self,
135        transaction: Transaction,
136        assigned_versions: &AssignedVersions,
137        component: Component,
138    ) -> (TransactionEffects, std::time::Duration) {
139        let executable = self.create_executable(transaction);
140        let start = std::time::Instant::now();
141        let effects = match component {
142            Component::Baseline => {
143                self.get_validator()
144                    .try_execute_immediately(
145                        &executable,
146                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
147                        &self.epoch_store,
148                    )
149                    .unwrap()
150                    .0
151            }
152            Component::WithTxManager => {
153                // Manually enqueue since we don't have consensus.
154                self.get_validator().execution_scheduler().enqueue(
155                    vec![(
156                        executable.clone().into(),
157                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
158                    )],
159                    &self.epoch_store,
160                );
161                self.get_validator()
162                    .wait_for_transaction_execution_for_testing(&executable)
163                    .await
164            }
165            Component::ValidatorWithoutConsensus | Component::ValidatorWithFakeConsensus => {
166                // Execute the transaction directly using try_execute_executable_for_test
167                let (signed_effects, _) = self
168                    .get_validator()
169                    .try_execute_executable_for_test(
170                        &executable,
171                        ExecutionEnv::new().with_assigned_versions(assigned_versions.clone()),
172                    )
173                    .await;
174                signed_effects.into_inner().into_data()
175            }
176            Component::CheckpointExecutor | Component::ExecutionOnly => {
177                unreachable!()
178            }
179        };
180        let elapsed = start.elapsed();
181        assert!(effects.status().is_ok());
182        (effects, elapsed)
183    }
184
185    pub(crate) async fn execute_transaction_in_memory(
186        &self,
187        store: InMemoryObjectStore,
188        transaction: Transaction,
189        assigned_versions: &AssignedVersions,
190    ) -> TransactionEffects {
191        let input_objects = transaction
192            .data()
193            .intent_message()
194            .value
195            .input_objects()
196            .unwrap();
197        let executable = self.create_executable(transaction);
198        let objects = store
199            .read_objects_for_execution(&executable.key(), assigned_versions, &input_objects)
200            .unwrap();
201
202        let (gas_status, input_objects) = sui_transaction_checks::check_certificate_input(
203            &executable,
204            objects,
205            self.epoch_store.protocol_config(),
206            self.epoch_store.reference_gas_price(),
207        )
208        .unwrap();
209        let (kind, signer, gas_data) = executable.transaction_data().execution_parts();
210        let (inner_temp_store, _, effects, _timings, _) =
211            self.epoch_store.executor().execute_transaction_to_effects(
212                &store,
213                self.epoch_store.protocol_config(),
214                self.get_validator().metrics.execution_metrics.clone(),
215                false,
216                ExecutionOrEarlyError::ok(None),
217                &self.epoch_store.epoch(),
218                0,
219                input_objects,
220                gas_data,
221                gas_status,
222                kind,
223                None, // compat_args
224                signer,
225                *executable.digest(),
226                &mut None,
227            );
228        assert!(effects.status().is_ok());
229        store.commit_objects(inner_temp_store);
230        effects
231    }
232
233    pub(crate) async fn build_checkpoints(
234        &self,
235        transactions: Vec<Transaction>,
236        mut all_effects: BTreeMap<TransactionDigest, TransactionEffects>,
237        checkpoint_size: usize,
238    ) -> Vec<(VerifiedCheckpoint, VerifiedCheckpointContents)> {
239        let mut builder = MockCheckpointBuilder::new(
240            self.get_validator()
241                .get_checkpoint_store()
242                .get_latest_certified_checkpoint()
243                .unwrap()
244                .unwrap(),
245        );
246        let mut checkpoints = vec![];
247        for transaction in transactions {
248            let effects = all_effects.remove(transaction.digest()).unwrap();
249            builder.push_transaction(VerifiedTransaction::new_unchecked(transaction), effects);
250            if builder.size() == checkpoint_size {
251                let (checkpoint, _, full_contents) = builder.build(self, 0);
252                checkpoints.push((checkpoint, full_contents));
253            }
254        }
255        if builder.size() > 0 {
256            let (checkpoint, _, full_contents) = builder.build(self, 0);
257            checkpoints.push((checkpoint, full_contents));
258        }
259        checkpoints
260    }
261
262    pub fn create_checkpoint_executor(&self) -> CheckpointExecutor {
263        let validator = self.get_validator();
264        CheckpointExecutor::new_for_tests(
265            self.epoch_store.clone(),
266            validator.get_checkpoint_store().clone(),
267            validator.clone(),
268            Arc::new(GlobalStateHasher::new_for_tests(
269                validator.get_global_state_hash_store().clone(),
270            )),
271        )
272    }
273
274    pub(crate) fn create_in_memory_store(&self) -> InMemoryObjectStore {
275        let objects: HashMap<_, _> = self
276            .get_validator()
277            .get_global_state_hash_store()
278            .iter_cached_live_object_set_for_testing(false)
279            .map(|o| match o {
280                LiveObject::Normal(object) => (object.id(), object),
281                LiveObject::Wrapped(_) => unreachable!(),
282            })
283            .collect();
284        InMemoryObjectStore::new(objects)
285    }
286
287    pub(crate) async fn assigned_shared_object_versions(
288        &self,
289        transactions: &[Transaction],
290    ) -> AssignedTxAndVersions {
291        let executables: Vec<_> = transactions
292            .iter()
293            .map(|tx| self.create_executable(tx.clone()))
294            .collect();
295        let assignables: Vec<_> = executables.iter().map(Schedulable::Transaction).collect();
296        self.epoch_store
297            .assign_shared_object_versions_idempotent(
298                self.get_validator().get_object_cache_reader().as_ref(),
299                assignables.iter(),
300            )
301            .unwrap()
302    }
303}
304
305impl ValidatorKeypairProvider for SingleValidator {
306    fn get_validator_key(&self, name: &AuthorityName) -> &dyn Signer<AuthoritySignature> {
307        assert_eq!(name, &self.get_validator().name);
308        &*self.get_validator().secret
309    }
310
311    fn get_committee(&self) -> &Committee {
312        self.epoch_store.committee().as_ref()
313    }
314}