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