Skip to main content

iota_single_node_benchmark/
single_node.rs

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