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