iota_core/authority/
authority_test_utils.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6use core::default::Default;
7
8use fastcrypto::{hash::MultisetHash, traits::KeyPair};
9use iota_types::{
10    crypto::{AccountKeyPair, AuthorityKeyPair},
11    messages_consensus::ConsensusTransaction,
12    utils::to_sender_signed_transaction,
13};
14
15use super::{test_authority_builder::TestAuthorityBuilder, *};
16use crate::{checkpoints::CheckpointServiceNoop, consensus_handler::SequencedConsensusTransaction};
17
18pub async fn send_and_confirm_transaction(
19    authority: &AuthorityState,
20    transaction: Transaction,
21) -> Result<(CertifiedTransaction, SignedTransactionEffects), IotaError> {
22    send_and_confirm_transaction_(
23        authority,
24        None, // no fullnode_key_pair
25        transaction,
26        false, // no shared objects
27    )
28    .await
29}
30pub async fn send_and_confirm_transaction_(
31    authority: &AuthorityState,
32    fullnode: Option<&AuthorityState>,
33    transaction: Transaction,
34    with_shared: bool, // transaction includes shared objects
35) -> Result<(CertifiedTransaction, SignedTransactionEffects), IotaError> {
36    let (txn, effects, _execution_error_opt) = send_and_confirm_transaction_with_execution_error(
37        authority,
38        fullnode,
39        transaction,
40        with_shared,
41        true,
42    )
43    .await?;
44    Ok((txn, effects))
45}
46
47pub async fn certify_transaction(
48    authority: &AuthorityState,
49    transaction: Transaction,
50) -> Result<VerifiedCertificate, IotaError> {
51    // Make the initial request
52    let epoch_store = authority.load_epoch_store_one_call_per_task();
53    // TODO: Move this check to a more appropriate place.
54    transaction.validity_check(epoch_store.protocol_config(), epoch_store.epoch())?;
55    let transaction = epoch_store.verify_transaction(transaction).unwrap();
56
57    let response = authority
58        .handle_transaction(&epoch_store, transaction.clone())
59        .await?;
60    let vote = response.status.into_signed_for_testing();
61
62    // Collect signatures from a quorum of authorities
63    let committee = authority.clone_committee_for_testing();
64    let certificate = CertifiedTransaction::new(transaction.into_message(), vec![vote], &committee)
65        .unwrap()
66        .try_into_verified_for_testing(&committee, &Default::default())
67        .unwrap();
68    Ok(certificate)
69}
70
71pub async fn execute_certificate_with_execution_error(
72    authority: &AuthorityState,
73    fullnode: Option<&AuthorityState>,
74    certificate: VerifiedCertificate,
75    with_shared: bool, // transaction includes shared objects
76    fake_consensus: bool,
77) -> Result<
78    (
79        CertifiedTransaction,
80        SignedTransactionEffects,
81        Option<ExecutionError>,
82    ),
83    IotaError,
84> {
85    // We also check the incremental effects of the transaction on the live object
86    // set against GlobalStateHasher for testing and regression detection.
87    // We must do this before sending to consensus, otherwise consensus may already
88    // lead to transaction execution and state change.
89    let state_acc =
90        GlobalStateHasher::new_for_tests(authority.get_global_state_hash_store().clone());
91    let mut state = state_acc.accumulate_cached_live_object_set_for_testing();
92
93    if with_shared {
94        if fake_consensus {
95            send_consensus(authority, &certificate).await;
96        } else {
97            // Just set object locks directly if send_consensus is not requested.
98            authority
99                .epoch_store_for_testing()
100                .assign_shared_object_versions_for_tests(
101                    authority.get_object_cache_reader().as_ref(),
102                    &[VerifiedExecutableTransaction::new_from_certificate(
103                        certificate.clone(),
104                    )],
105                )?;
106        }
107        if let Some(fullnode) = fullnode {
108            fullnode
109                .epoch_store_for_testing()
110                .assign_shared_object_versions_for_tests(
111                    fullnode.get_object_cache_reader().as_ref(),
112                    &[VerifiedExecutableTransaction::new_from_certificate(
113                        certificate.clone(),
114                    )],
115                )?;
116        }
117    }
118
119    // Submit the confirmation. *Now* execution actually happens, and it should fail
120    // when we try to look up our dummy module. we unfortunately don't get a
121    // very descriptive error message, but we can at least see that something went
122    // wrong inside the VM
123    let (result, execution_error_opt) = authority.try_execute_for_test(&certificate)?;
124    let state_after = state_acc.accumulate_cached_live_object_set_for_testing();
125    let effects_acc = state_acc.accumulate_effects(&[result.inner().data().clone()]);
126    state.union(&effects_acc);
127
128    assert_eq!(state_after.digest(), state.digest());
129
130    if let Some(fullnode) = fullnode {
131        fullnode.try_execute_for_test(&certificate)?;
132    }
133    Ok((
134        certificate.into_inner(),
135        result.into_inner(),
136        execution_error_opt,
137    ))
138}
139
140pub async fn send_and_confirm_transaction_with_execution_error(
141    authority: &AuthorityState,
142    fullnode: Option<&AuthorityState>,
143    transaction: Transaction,
144    with_shared: bool,    // transaction includes shared objects
145    fake_consensus: bool, // runs consensus handler if true
146) -> Result<
147    (
148        CertifiedTransaction,
149        SignedTransactionEffects,
150        Option<ExecutionError>,
151    ),
152    IotaError,
153> {
154    let certificate = certify_transaction(authority, transaction).await?;
155    execute_certificate_with_execution_error(
156        authority,
157        fullnode,
158        certificate,
159        with_shared,
160        fake_consensus,
161    )
162    .await
163}
164
165pub async fn init_state_validator_with_fullnode() -> (Arc<AuthorityState>, Arc<AuthorityState>) {
166    use iota_types::crypto::get_authority_key_pair;
167
168    let validator = TestAuthorityBuilder::new().build().await;
169    let fullnode_key_pair = get_authority_key_pair().1;
170    let fullnode = TestAuthorityBuilder::new()
171        .with_keypair(&fullnode_key_pair)
172        .build()
173        .await;
174    (validator, fullnode)
175}
176
177pub async fn init_state_with_committee(
178    genesis: &Genesis,
179    authority_key: &AuthorityKeyPair,
180) -> Arc<AuthorityState> {
181    TestAuthorityBuilder::new()
182        .with_genesis_and_keypair(genesis, authority_key)
183        .build()
184        .await
185}
186
187pub async fn init_state_with_ids<I: IntoIterator<Item = (IotaAddress, ObjectID)>>(
188    objects: I,
189) -> Arc<AuthorityState> {
190    let state = TestAuthorityBuilder::new().build().await;
191    for (address, object_id) in objects {
192        let obj = Object::with_id_owner_for_testing(object_id, address);
193        // TODO: Make this part of genesis initialization instead of explicit insert.
194        state.insert_genesis_object(obj).await;
195    }
196    state
197}
198
199pub async fn init_state_with_ids_and_versions<
200    I: IntoIterator<Item = (IotaAddress, ObjectID, SequenceNumber)>,
201>(
202    objects: I,
203) -> Arc<AuthorityState> {
204    let state = TestAuthorityBuilder::new().build().await;
205    for (address, object_id, version) in objects {
206        let obj =
207            Object::with_id_owner_version_for_testing(object_id, version, Owner::Address(address));
208        state.insert_genesis_object(obj).await;
209    }
210    state
211}
212
213pub async fn init_state_with_objects<I: IntoIterator<Item = Object>>(
214    objects: I,
215) -> Arc<AuthorityState> {
216    let dir = tempfile::TempDir::new().unwrap();
217    let network_config =
218        iota_swarm_config::network_config_builder::ConfigBuilder::new(&dir).build();
219    let genesis = network_config.genesis;
220    let keypair = network_config.validator_configs[0]
221        .authority_key_pair()
222        .copy();
223    init_state_with_objects_and_committee(objects, &genesis, &keypair).await
224}
225
226pub async fn init_state_with_objects_and_committee<I: IntoIterator<Item = Object>>(
227    objects: I,
228    genesis: &Genesis,
229    authority_key: &AuthorityKeyPair,
230) -> Arc<AuthorityState> {
231    let state = init_state_with_committee(genesis, authority_key).await;
232    for o in objects {
233        state.insert_genesis_object(o).await;
234    }
235    state
236}
237
238pub async fn init_state_with_object_id(
239    address: IotaAddress,
240    object: ObjectID,
241) -> Arc<AuthorityState> {
242    init_state_with_ids(std::iter::once((address, object))).await
243}
244
245pub async fn init_state_with_ids_and_expensive_checks<
246    I: IntoIterator<Item = (IotaAddress, ObjectID)>,
247>(
248    objects: I,
249    config: ExpensiveSafetyCheckConfig,
250) -> Arc<AuthorityState> {
251    let state = TestAuthorityBuilder::new()
252        .with_expensive_safety_checks(config)
253        .build()
254        .await;
255    for (address, object_id) in objects {
256        let obj = Object::with_id_owner_for_testing(object_id, address);
257        // TODO: Make this part of genesis initialization instead of explicit insert.
258        state.insert_genesis_object(obj).await;
259    }
260    state
261}
262
263pub fn init_transfer_transaction(
264    authority_state: &AuthorityState,
265    sender: IotaAddress,
266    secret: &AccountKeyPair,
267    recipient: IotaAddress,
268    object_ref: ObjectRef,
269    gas_object_ref: ObjectRef,
270    gas_budget: u64,
271    gas_price: u64,
272) -> VerifiedTransaction {
273    let data = TransactionData::new_transfer(
274        recipient,
275        object_ref,
276        sender,
277        gas_object_ref,
278        gas_budget,
279        gas_price,
280    );
281    let tx = to_sender_signed_transaction(data, secret);
282    authority_state
283        .epoch_store_for_testing()
284        .verify_transaction(tx)
285        .unwrap()
286}
287
288pub fn init_certified_transfer_transaction(
289    sender: IotaAddress,
290    secret: &AccountKeyPair,
291    recipient: IotaAddress,
292    object_ref: ObjectRef,
293    gas_object_ref: ObjectRef,
294    authority_state: &AuthorityState,
295) -> VerifiedCertificate {
296    let rgp = authority_state.reference_gas_price_for_testing().unwrap();
297    let transfer_transaction = init_transfer_transaction(
298        authority_state,
299        sender,
300        secret,
301        recipient,
302        object_ref,
303        gas_object_ref,
304        rgp * TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
305        rgp,
306    );
307    init_certified_transaction(transfer_transaction.into(), authority_state)
308}
309
310pub fn init_certified_transaction(
311    transaction: Transaction,
312    authority_state: &AuthorityState,
313) -> VerifiedCertificate {
314    let epoch_store = authority_state.epoch_store_for_testing();
315    let transaction = epoch_store.verify_transaction(transaction).unwrap();
316
317    let vote = VerifiedSignedTransaction::new(
318        0,
319        transaction.clone(),
320        authority_state.name,
321        &*authority_state.secret,
322    );
323    CertifiedTransaction::new(
324        transaction.into_message(),
325        vec![vote.auth_sig().clone()],
326        epoch_store.committee(),
327    )
328    .unwrap()
329    .try_into_verified_for_testing(epoch_store.committee(), &Default::default())
330    .unwrap()
331}
332
333pub async fn certify_shared_obj_transaction_no_execution(
334    authority: &AuthorityState,
335    transaction: Transaction,
336) -> Result<VerifiedCertificate, IotaError> {
337    let epoch_store = authority.load_epoch_store_one_call_per_task();
338    let transaction = epoch_store.verify_transaction(transaction).unwrap();
339    let response = authority
340        .handle_transaction(&epoch_store, transaction.clone())
341        .await?;
342    let vote = response.status.into_signed_for_testing();
343
344    // Collect signatures from a quorum of authorities
345    let committee = authority.clone_committee_for_testing();
346    let certificate =
347        CertifiedTransaction::new(transaction.into_message(), vec![vote.clone()], &committee)
348            .unwrap()
349            .try_into_verified_for_testing(&committee, &Default::default())
350            .unwrap();
351
352    send_consensus_no_execution(authority, &certificate).await;
353
354    Ok(certificate)
355}
356
357pub async fn enqueue_all_and_execute_all(
358    authority: &AuthorityState,
359    certificates: Vec<VerifiedCertificate>,
360) -> Result<Vec<TransactionEffects>, IotaError> {
361    authority.enqueue_certificates_for_execution(
362        certificates.clone(),
363        &authority.epoch_store_for_testing(),
364    );
365    let mut output = Vec::new();
366    for cert in certificates {
367        let effects = authority.notify_read_effects(&cert).await?;
368        output.push(effects);
369    }
370    Ok(output)
371}
372
373pub async fn execute_sequenced_certificate_to_effects(
374    authority: &AuthorityState,
375    certificate: VerifiedCertificate,
376) -> Result<(TransactionEffects, Option<ExecutionError>), IotaError> {
377    authority.enqueue_certificates_for_execution(
378        vec![certificate.clone()],
379        &authority.epoch_store_for_testing(),
380    );
381
382    let (result, execution_error_opt) = authority.try_execute_for_test(&certificate)?;
383    let effects = result.inner().data().clone();
384    Ok((effects, execution_error_opt))
385}
386
387pub async fn send_consensus(authority: &AuthorityState, cert: &VerifiedCertificate) {
388    let transaction = SequencedConsensusTransaction::new_test(
389        ConsensusTransaction::new_certificate_message(&authority.name, cert.clone().into_inner()),
390    );
391
392    let certs = authority
393        .epoch_store_for_testing()
394        .process_consensus_transactions_for_tests(
395            vec![transaction],
396            &Arc::new(CheckpointServiceNoop {}),
397            authority.get_object_cache_reader().as_ref(),
398            authority.get_transaction_cache_reader().as_ref(),
399            &authority.metrics,
400            true,
401        )
402        .await
403        .unwrap();
404
405    authority
406        .transaction_manager()
407        .enqueue(certs, &authority.epoch_store_for_testing());
408}
409
410pub async fn send_consensus_no_execution(authority: &AuthorityState, cert: &VerifiedCertificate) {
411    let transaction = SequencedConsensusTransaction::new_test(
412        ConsensusTransaction::new_certificate_message(&authority.name, cert.clone().into_inner()),
413    );
414
415    // Call process_consensus_transaction() instead of
416    // handle_consensus_transaction(), to avoid actually executing cert.
417    // This allows testing cert execution independently.
418    authority
419        .epoch_store_for_testing()
420        .process_consensus_transactions_for_tests(
421            vec![transaction],
422            &Arc::new(CheckpointServiceNoop {}),
423            authority.get_object_cache_reader().as_ref(),
424            authority.get_transaction_cache_reader().as_ref(),
425            &authority.metrics,
426            true,
427        )
428        .await
429        .unwrap();
430}
431
432pub async fn send_batch_consensus_no_execution(
433    authority: &AuthorityState,
434    certificates: &[VerifiedCertificate],
435    skip_consensus_commit_prologue_in_test: bool,
436) -> Vec<VerifiedExecutableTransaction> {
437    let transactions = certificates
438        .iter()
439        .map(|cert| {
440            SequencedConsensusTransaction::new_test(ConsensusTransaction::new_certificate_message(
441                &authority.name,
442                cert.clone().into_inner(),
443            ))
444        })
445        .collect();
446
447    // Call process_consensus_transaction() instead of
448    // handle_consensus_transaction(), to avoid actually executing cert.
449    // This allows testing cert execution independently.
450    authority
451        .epoch_store_for_testing()
452        .process_consensus_transactions_for_tests(
453            transactions,
454            &Arc::new(CheckpointServiceNoop {}),
455            authority.get_object_cache_reader().as_ref(),
456            authority.get_transaction_cache_reader().as_ref(),
457            &authority.metrics,
458            skip_consensus_commit_prologue_in_test,
459        )
460        .await
461        .unwrap()
462}