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