iota_core/
test_utils.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{sync::Arc, time::Duration};
6
7use fastcrypto::{hash::MultisetHash, traits::KeyPair};
8use iota_sdk_types::crypto::{Intent, IntentScope};
9use iota_types::{
10    base_types::{
11        AuthorityName, ExecutionDigests, Identifier, IotaAddress, ObjectID, ObjectRef,
12        TransactionDigest, random_object_ref,
13    },
14    committee::Committee,
15    crypto::{
16        AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo,
17        AuthoritySignature, Signer,
18    },
19    effects::{SignedTransactionEffects, TestEffectsBuilder},
20    error::IotaError,
21    message_envelope::Message,
22    transaction::{
23        CallArg, CertifiedTransaction, ObjectArg, SignedTransaction,
24        TEST_ONLY_GAS_UNIT_FOR_TRANSFER, Transaction, TransactionData,
25    },
26    utils::{create_fake_transaction, to_sender_signed_transaction},
27};
28use move_core_types::account_address::AccountAddress;
29use tokio::time::timeout;
30use tracing::{info, warn};
31
32use crate::{authority::AuthorityState, global_state_hasher::GlobalStateHasher};
33
34const WAIT_FOR_TX_TIMEOUT: Duration = Duration::from_secs(15);
35
36pub async fn send_and_confirm_transaction(
37    authority: &AuthorityState,
38    fullnode: Option<&AuthorityState>,
39    transaction: Transaction,
40) -> Result<(CertifiedTransaction, SignedTransactionEffects), IotaError> {
41    // Make the initial request
42    let epoch_store = authority.load_epoch_store_one_call_per_task();
43    transaction.validity_check(epoch_store.protocol_config(), epoch_store.epoch())?;
44    let transaction = epoch_store.verify_transaction(transaction)?;
45    let response = authority
46        .handle_transaction(&epoch_store, transaction.clone())
47        .await?;
48    let vote = response.status.into_signed_for_testing();
49
50    // Collect signatures from a quorum of authorities
51    let committee = authority.clone_committee_for_testing();
52    let certificate = CertifiedTransaction::new(transaction.into_message(), vec![vote], &committee)
53        .unwrap()
54        .try_into_verified_for_testing(&committee, &Default::default())
55        .unwrap();
56
57    // Submit the confirmation. *Now* execution actually happens, and it should fail
58    // when we try to look up our dummy module. we unfortunately don't get a
59    // very descriptive error message, but we can at least see that something went
60    // wrong inside the VM
61    //
62    // We also check the incremental effects of the transaction on the live object
63    // set against GlobalStateHasher for testing and regression detection
64    let state_acc =
65        GlobalStateHasher::new_for_tests(authority.get_global_state_hash_store().clone());
66    let mut state = state_acc.accumulate_cached_live_object_set_for_testing();
67    let (result, _execution_error_opt) = authority.try_execute_for_test(&certificate)?;
68    let state_after = state_acc.accumulate_cached_live_object_set_for_testing();
69    let effects_acc = state_acc.accumulate_effects(&[result.inner().data().clone()]);
70    state.union(&effects_acc);
71
72    assert_eq!(state_after.digest(), state.digest());
73
74    if let Some(fullnode) = fullnode {
75        fullnode.try_execute_for_test(&certificate)?;
76    }
77    Ok((certificate.into_inner(), result.into_inner()))
78}
79
80#[cfg(test)]
81pub(crate) fn init_state_parameters_from_rng<R>(
82    rng: &mut R,
83) -> (iota_config::genesis::Genesis, AuthorityKeyPair)
84where
85    R: rand::CryptoRng + rand::RngCore,
86{
87    let dir = iota_macros::nondeterministic!(tempfile::TempDir::new().unwrap());
88    let network_config = iota_swarm_config::network_config_builder::ConfigBuilder::new(&dir)
89        .rng(rng)
90        .build();
91    let genesis = network_config.genesis;
92    let authority_key = network_config.validator_configs[0]
93        .authority_key_pair()
94        .copy();
95
96    (genesis, authority_key)
97}
98
99pub async fn wait_for_tx(digest: TransactionDigest, state: Arc<AuthorityState>) {
100    match timeout(
101        WAIT_FOR_TX_TIMEOUT,
102        state
103            .get_transaction_cache_reader()
104            .try_notify_read_executed_effects(&[digest]),
105    )
106    .await
107    {
108        Ok(_) => info!(?digest, "digest found"),
109        Err(e) => {
110            warn!(?digest, "digest not found!");
111            panic!("timed out waiting for effects of digest! {e}");
112        }
113    }
114}
115
116pub async fn wait_for_all_txes(digests: Vec<TransactionDigest>, state: Arc<AuthorityState>) {
117    match timeout(
118        WAIT_FOR_TX_TIMEOUT,
119        state
120            .get_transaction_cache_reader()
121            .try_notify_read_executed_effects(&digests),
122    )
123    .await
124    {
125        Ok(_) => info!(?digests, "all digests found"),
126        Err(e) => {
127            warn!(?digests, "some digests not found!");
128            panic!("timed out waiting for effects of digests! {e}");
129        }
130    }
131}
132
133pub fn create_fake_cert_and_effect_digest<'a>(
134    signers: impl Iterator<
135        Item = (
136            &'a AuthorityName,
137            &'a (dyn Signer<AuthoritySignature> + Send + Sync),
138        ),
139    >,
140    committee: &Committee,
141) -> (ExecutionDigests, CertifiedTransaction) {
142    let transaction = create_fake_transaction();
143    let cert = CertifiedTransaction::new(
144        transaction.data().clone(),
145        signers
146            .map(|(name, signer)| {
147                AuthoritySignInfo::new(
148                    committee.epoch,
149                    transaction.data(),
150                    Intent::iota_app(IntentScope::SenderSignedTransaction),
151                    *name,
152                    signer,
153                )
154            })
155            .collect(),
156        committee,
157    )
158    .unwrap();
159    let effects = TestEffectsBuilder::new(transaction.data()).build();
160    (
161        ExecutionDigests::new(*transaction.digest(), effects.digest()),
162        cert,
163    )
164}
165
166pub fn make_transfer_iota_transaction(
167    gas_object: ObjectRef,
168    recipient: IotaAddress,
169    amount: Option<u64>,
170    sender: IotaAddress,
171    keypair: &AccountKeyPair,
172    gas_price: u64,
173) -> Transaction {
174    let data = TransactionData::new_transfer_iota(
175        recipient,
176        sender,
177        amount,
178        gas_object,
179        gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
180        gas_price,
181    );
182    to_sender_signed_transaction(data, keypair)
183}
184
185pub fn make_pay_iota_transaction(
186    gas_object: ObjectRef,
187    coins: Vec<ObjectRef>,
188    recipients: Vec<IotaAddress>,
189    amounts: Vec<u64>,
190    sender: IotaAddress,
191    keypair: &AccountKeyPair,
192    gas_price: u64,
193    gas_budget: u64,
194) -> Transaction {
195    let data = TransactionData::new_pay_iota(
196        sender, coins, recipients, amounts, gas_object, gas_budget, gas_price,
197    )
198    .unwrap();
199    to_sender_signed_transaction(data, keypair)
200}
201
202pub fn make_transfer_object_transaction(
203    object_ref: ObjectRef,
204    gas_object: ObjectRef,
205    sender: IotaAddress,
206    keypair: &AccountKeyPair,
207    recipient: IotaAddress,
208    gas_price: u64,
209) -> Transaction {
210    let data = TransactionData::new_transfer(
211        recipient,
212        object_ref,
213        sender,
214        gas_object,
215        gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER * 10,
216        gas_price,
217    );
218    to_sender_signed_transaction(data, keypair)
219}
220
221pub fn make_transfer_object_move_transaction(
222    src: IotaAddress,
223    keypair: &AccountKeyPair,
224    dest: IotaAddress,
225    object_ref: ObjectRef,
226    framework_obj_id: ObjectID,
227    gas_object_ref: ObjectRef,
228    gas_budget_in_units: u64,
229    gas_price: u64,
230) -> Transaction {
231    let args = vec![
232        CallArg::Object(ObjectArg::ImmOrOwnedObject(object_ref)),
233        CallArg::Pure(bcs::to_bytes(&AccountAddress::new(dest.into_bytes())).unwrap()),
234    ];
235
236    to_sender_signed_transaction(
237        TransactionData::new_move_call(
238            src,
239            framework_obj_id,
240            Identifier::from_static("object_basics"),
241            Identifier::from_static("transfer"),
242            Vec::new(),
243            gas_object_ref,
244            args,
245            gas_budget_in_units * gas_price,
246            gas_price,
247        )
248        .unwrap(),
249        keypair,
250    )
251}
252
253/// Make a dummy tx that uses random object refs.
254pub fn make_dummy_tx(
255    receiver: IotaAddress,
256    sender: IotaAddress,
257    sender_sec: &AccountKeyPair,
258) -> Transaction {
259    Transaction::from_data_and_signer(
260        TransactionData::new_transfer(
261            receiver,
262            random_object_ref(),
263            sender,
264            random_object_ref(),
265            TEST_ONLY_GAS_UNIT_FOR_TRANSFER * 10,
266            10,
267        ),
268        vec![sender_sec],
269    )
270}
271
272/// Make a cert using an arbitrarily large committee.
273pub fn make_cert_with_large_committee(
274    committee: &Committee,
275    key_pairs: &[AuthorityKeyPair],
276    transaction: &Transaction,
277) -> CertifiedTransaction {
278    // assumes equal weighting.
279    let len = committee.voting_rights.len();
280    assert_eq!(len, key_pairs.len());
281    let count = (len * 2).div_ceil(3);
282
283    let sigs: Vec<_> = key_pairs
284        .iter()
285        .take(count)
286        .map(|key_pair| {
287            SignedTransaction::new(
288                committee.epoch(),
289                transaction.clone().into_data(),
290                key_pair,
291                AuthorityPublicKeyBytes::from(key_pair.public()),
292            )
293            .auth_sig()
294            .clone()
295        })
296        .collect();
297
298    let cert = CertifiedTransaction::new(transaction.clone().into_data(), sigs, committee).unwrap();
299    cert.verify_signatures_authenticated(committee, &Default::default())
300        .unwrap();
301    cert
302}