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