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