sui_core/authority/
authority_test_utils.rs

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