sui_core/
test_utils.rs

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