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