Skip to main content

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                std::collections::BTreeMap::new(),
221                gas_data,
222                gas_status,
223                kind,
224                None, // compat_args
225                signer,
226                *executable.digest(),
227                &mut None,
228            );
229        assert!(effects.status().is_ok());
230        store.commit_objects(inner_temp_store);
231        effects
232    }
233
234    pub(crate) async fn build_checkpoints(
235        &self,
236        transactions: Vec<Transaction>,
237        mut all_effects: BTreeMap<TransactionDigest, TransactionEffects>,
238        checkpoint_size: usize,
239    ) -> Vec<(VerifiedCheckpoint, VerifiedCheckpointContents)> {
240        let mut builder = MockCheckpointBuilder::new(
241            self.get_validator()
242                .get_checkpoint_store()
243                .get_latest_certified_checkpoint()
244                .unwrap()
245                .unwrap(),
246        );
247        let mut checkpoints = vec![];
248        for transaction in transactions {
249            let effects = all_effects.remove(transaction.digest()).unwrap();
250            builder.push_transaction(VerifiedTransaction::new_unchecked(transaction), effects);
251            if builder.size() == checkpoint_size {
252                let (checkpoint, _, full_contents) = builder.build(self, 0);
253                checkpoints.push((checkpoint, full_contents));
254            }
255        }
256        if builder.size() > 0 {
257            let (checkpoint, _, full_contents) = builder.build(self, 0);
258            checkpoints.push((checkpoint, full_contents));
259        }
260        checkpoints
261    }
262
263    pub fn create_checkpoint_executor(&self) -> CheckpointExecutor {
264        let validator = self.get_validator();
265        CheckpointExecutor::new_for_tests(
266            self.epoch_store.clone(),
267            validator.get_checkpoint_store().clone(),
268            validator.clone(),
269            Arc::new(GlobalStateHasher::new_for_tests(
270                validator.get_global_state_hash_store().clone(),
271            )),
272        )
273    }
274
275    pub(crate) fn create_in_memory_store(&self) -> InMemoryObjectStore {
276        let objects: HashMap<_, _> = self
277            .get_validator()
278            .get_global_state_hash_store()
279            .iter_cached_live_object_set_for_testing(false)
280            .map(|o| match o {
281                LiveObject::Normal(object) => (object.id(), object),
282                LiveObject::Wrapped(_) => unreachable!(),
283            })
284            .collect();
285        InMemoryObjectStore::new(objects)
286    }
287
288    pub(crate) async fn assigned_shared_object_versions(
289        &self,
290        transactions: &[Transaction],
291    ) -> AssignedTxAndVersions {
292        let executables: Vec<_> = transactions
293            .iter()
294            .map(|tx| self.create_executable(tx.clone()))
295            .collect();
296        let assignables: Vec<_> = executables.iter().map(Schedulable::Transaction).collect();
297        self.epoch_store
298            .assign_shared_object_versions_idempotent(
299                self.get_validator().get_object_cache_reader().as_ref(),
300                assignables.iter(),
301            )
302            .unwrap()
303    }
304}
305
306impl ValidatorKeypairProvider for SingleValidator {
307    fn get_validator_key(&self, name: &AuthorityName) -> &dyn Signer<AuthoritySignature> {
308        assert_eq!(name, &self.get_validator().name);
309        &*self.get_validator().secret
310    }
311
312    fn get_committee(&self) -> &Committee {
313        self.epoch_store.committee().as_ref()
314    }
315}