1use std::{collections::BTreeMap, path::PathBuf, sync::Arc, time::Duration};
6
7use fastcrypto::{hash::MultisetHash, traits::KeyPair};
8use futures::future::join_all;
9use iota_config::{genesis::Genesis, local_ip_utils, node::AuthorityOverloadConfig};
10use iota_framework::BuiltInFramework;
11use iota_genesis_builder::{
12 genesis_build_effects::GenesisBuildEffects, validator_info::ValidatorInfo,
13};
14use iota_move_build::{BuildConfig, CompiledPackage, IotaPackageHooks};
15use iota_protocol_config::ProtocolConfig;
16use iota_types::{
17 base_types::{
18 AuthorityName, ExecutionDigests, IotaAddress, ObjectID, ObjectRef, TransactionDigest,
19 random_object_ref,
20 },
21 committee::Committee,
22 crypto::{
23 AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, AuthoritySignInfo,
24 AuthoritySignature, IotaKeyPair, NetworkKeyPair, Signer, generate_proof_of_possession,
25 get_key_pair,
26 },
27 effects::{SignedTransactionEffects, TestEffectsBuilder},
28 error::IotaError,
29 message_envelope::Message,
30 object::Object,
31 signature_verification::VerifiedDigestCache,
32 transaction::{
33 CallArg, CertifiedTransaction, ObjectArg, SignedTransaction,
34 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, Transaction, TransactionData,
35 },
36 utils::{create_fake_transaction, to_sender_signed_transaction},
37};
38use move_core_types::{account_address::AccountAddress, ident_str};
39use shared_crypto::intent::{Intent, IntentScope};
40use tokio::time::timeout;
41use tracing::{info, warn};
42
43use crate::{
44 authority::{AuthorityState, test_authority_builder::TestAuthorityBuilder},
45 authority_aggregator::{AuthorityAggregator, AuthorityAggregatorBuilder, TimeoutConfig},
46 state_accumulator::StateAccumulator,
47 test_authority_clients::LocalAuthorityClient,
48};
49
50const WAIT_FOR_TX_TIMEOUT: Duration = Duration::from_secs(15);
51
52pub async fn send_and_confirm_transaction(
53 authority: &AuthorityState,
54 fullnode: Option<&AuthorityState>,
55 transaction: Transaction,
56) -> Result<(CertifiedTransaction, SignedTransactionEffects), IotaError> {
57 let epoch_store = authority.load_epoch_store_one_call_per_task();
59 transaction.validity_check(epoch_store.protocol_config(), epoch_store.epoch())?;
60 let transaction = epoch_store.verify_transaction(transaction)?;
61 let response = authority
62 .handle_transaction(&epoch_store, transaction.clone())
63 .await?;
64 let vote = response.status.into_signed_for_testing();
65
66 let committee = authority.clone_committee_for_testing();
68 let certificate =
69 CertifiedTransaction::new(transaction.into_message(), vec![vote.clone()], &committee)
70 .unwrap()
71 .try_into_verified_for_testing(&committee, &Default::default())
72 .unwrap();
73
74 let state_acc = StateAccumulator::new_for_tests(authority.get_accumulator_store().clone());
82 let mut state = state_acc.accumulate_live_object_set();
83 let (result, _execution_error_opt) = authority.try_execute_for_test(&certificate).await?;
84 let state_after = state_acc.accumulate_live_object_set();
85 let effects_acc = state_acc.accumulate_effects(vec![result.inner().data().clone()]);
86 state.union(&effects_acc);
87
88 assert_eq!(state_after.digest(), state.digest());
89
90 if let Some(fullnode) = fullnode {
91 fullnode.try_execute_for_test(&certificate).await?;
92 }
93 Ok((certificate.into_inner(), result.into_inner()))
94}
95
96#[cfg(test)]
97pub(crate) fn init_state_parameters_from_rng<R>(rng: &mut R) -> (Genesis, AuthorityKeyPair)
98where
99 R: rand::CryptoRng + rand::RngCore,
100{
101 let dir = iota_macros::nondeterministic!(tempfile::TempDir::new().unwrap());
102 let network_config = iota_swarm_config::network_config_builder::ConfigBuilder::new(&dir)
103 .rng(rng)
104 .build();
105 let genesis = network_config.genesis;
106 let authority_key = network_config.validator_configs[0]
107 .authority_key_pair()
108 .copy();
109
110 (genesis, authority_key)
111}
112
113pub async fn wait_for_tx(digest: TransactionDigest, state: Arc<AuthorityState>) {
114 match timeout(
115 WAIT_FOR_TX_TIMEOUT,
116 state
117 .get_transaction_cache_reader()
118 .notify_read_executed_effects(&[digest]),
119 )
120 .await
121 {
122 Ok(_) => info!(?digest, "digest found"),
123 Err(e) => {
124 warn!(?digest, "digest not found!");
125 panic!("timed out waiting for effects of digest! {e}");
126 }
127 }
128}
129
130pub async fn wait_for_all_txes(digests: Vec<TransactionDigest>, state: Arc<AuthorityState>) {
131 match timeout(
132 WAIT_FOR_TX_TIMEOUT,
133 state
134 .get_transaction_cache_reader()
135 .notify_read_executed_effects(&digests),
136 )
137 .await
138 {
139 Ok(_) => info!(?digests, "all digests found"),
140 Err(e) => {
141 warn!(?digests, "some digests not found!");
142 panic!("timed out waiting for effects of digests! {e}");
143 }
144 }
145}
146
147pub fn create_fake_cert_and_effect_digest<'a>(
148 signers: impl Iterator<
149 Item = (
150 &'a AuthorityName,
151 &'a (dyn Signer<AuthoritySignature> + Send + Sync),
152 ),
153 >,
154 committee: &Committee,
155) -> (ExecutionDigests, CertifiedTransaction) {
156 let transaction = create_fake_transaction();
157 let cert = CertifiedTransaction::new(
158 transaction.data().clone(),
159 signers
160 .map(|(name, signer)| {
161 AuthoritySignInfo::new(
162 committee.epoch,
163 transaction.data(),
164 Intent::iota_app(IntentScope::SenderSignedTransaction),
165 *name,
166 signer,
167 )
168 })
169 .collect(),
170 committee,
171 )
172 .unwrap();
173 let effects = TestEffectsBuilder::new(transaction.data()).build();
174 (
175 ExecutionDigests::new(*transaction.digest(), effects.digest()),
176 cert,
177 )
178}
179
180pub fn compile_basics_package() -> CompiledPackage {
181 compile_example_package("../../examples/move/basics")
182}
183
184pub fn compile_managed_coin_package() -> CompiledPackage {
185 compile_example_package("../../crates/iota-core/src/unit_tests/data/managed_coin")
186}
187
188pub fn compile_example_package(relative_path: &str) -> CompiledPackage {
189 move_package::package_hooks::register_package_hooks(Box::new(IotaPackageHooks));
190 let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
191 path.push(relative_path);
192
193 BuildConfig::new_for_testing().build(&path).unwrap()
194}
195
196async fn init_genesis(
197 committee_size: usize,
198 mut genesis_objects: Vec<Object>,
199) -> (
200 Genesis,
201 Vec<(AuthorityPublicKeyBytes, AuthorityKeyPair)>,
202 ObjectID,
203) {
204 let modules: Vec<_> = compile_basics_package().get_modules().cloned().collect();
206 let genesis_move_packages: Vec<_> = BuiltInFramework::genesis_move_packages().collect();
207 let config = ProtocolConfig::get_for_max_version_UNSAFE();
208 let pkg = Object::new_package(
209 &modules,
210 TransactionDigest::genesis_marker(),
211 config.max_move_package_size(),
212 config.move_binary_format_version(),
213 &genesis_move_packages,
214 )
215 .unwrap();
216 let pkg_id = pkg.id();
217 genesis_objects.push(pkg);
218
219 let mut builder = iota_genesis_builder::Builder::new().add_objects(genesis_objects);
220 let mut key_pairs = Vec::new();
221 for i in 0..committee_size {
222 let authority_key_pair: AuthorityKeyPair = get_key_pair().1;
223 let authority_pubkey_bytes = authority_key_pair.public().into();
224 let protocol_key_pair: NetworkKeyPair = get_key_pair().1;
225 let protocol_pubkey = protocol_key_pair.public().clone();
226 let account_key_pair: IotaKeyPair = get_key_pair::<AccountKeyPair>().1.into();
227 let network_key_pair: NetworkKeyPair = get_key_pair().1;
228 let validator_info = ValidatorInfo {
229 name: format!("validator-{i}"),
230 authority_key: authority_pubkey_bytes,
231 protocol_key: protocol_pubkey,
232 account_address: IotaAddress::from(&account_key_pair.public()),
233 network_key: network_key_pair.public().clone(),
234 gas_price: 1,
235 commission_rate: 0,
236 network_address: local_ip_utils::new_local_tcp_address_for_testing(),
237 p2p_address: local_ip_utils::new_local_udp_address_for_testing(),
238 primary_address: local_ip_utils::new_local_udp_address_for_testing(),
239 description: String::new(),
240 image_url: String::new(),
241 project_url: String::new(),
242 };
243 let pop =
244 generate_proof_of_possession(&authority_key_pair, (&account_key_pair.public()).into());
245 builder = builder.add_validator(validator_info, pop);
246 key_pairs.push((authority_pubkey_bytes, authority_key_pair));
247 }
248 for (_, key) in &key_pairs {
249 builder = builder.add_validator_signature(key);
250 }
251
252 let GenesisBuildEffects { genesis, .. } = builder.build();
253 (genesis, key_pairs, pkg_id)
254}
255
256pub async fn init_local_authorities(
257 committee_size: usize,
258 genesis_objects: Vec<Object>,
259) -> (
260 AuthorityAggregator<LocalAuthorityClient>,
261 Vec<Arc<AuthorityState>>,
262 Genesis,
263 ObjectID,
264) {
265 let (genesis, key_pairs, framework) = init_genesis(committee_size, genesis_objects).await;
266 let authorities = join_all(key_pairs.iter().map(|(_, key_pair)| {
267 TestAuthorityBuilder::new()
268 .with_genesis_and_keypair(&genesis, key_pair)
269 .build()
270 }))
271 .await;
272 let aggregator = init_local_authorities_with_genesis(&genesis, authorities.clone()).await;
273 (aggregator, authorities, genesis, framework)
274}
275
276pub async fn init_local_authorities_with_overload_thresholds(
277 committee_size: usize,
278 genesis_objects: Vec<Object>,
279 overload_thresholds: AuthorityOverloadConfig,
280) -> (
281 AuthorityAggregator<LocalAuthorityClient>,
282 Vec<Arc<AuthorityState>>,
283 Genesis,
284 ObjectID,
285) {
286 let (genesis, key_pairs, framework) = init_genesis(committee_size, genesis_objects).await;
287 let authorities = join_all(key_pairs.iter().map(|(_, key_pair)| {
288 TestAuthorityBuilder::new()
289 .with_genesis_and_keypair(&genesis, key_pair)
290 .with_authority_overload_config(overload_thresholds.clone())
291 .build()
292 }))
293 .await;
294 let aggregator = init_local_authorities_with_genesis(&genesis, authorities.clone()).await;
295 (aggregator, authorities, genesis, framework)
296}
297
298pub async fn init_local_authorities_with_genesis(
299 genesis: &Genesis,
300 authorities: Vec<Arc<AuthorityState>>,
301) -> AuthorityAggregator<LocalAuthorityClient> {
302 telemetry_subscribers::init_for_testing();
303 let mut clients = BTreeMap::new();
304 for state in authorities {
305 let name = state.name;
306 let client = LocalAuthorityClient::new_from_authority(state);
307 clients.insert(name, client);
308 }
309 let timeouts = TimeoutConfig {
310 pre_quorum_timeout: Duration::from_secs(5),
311 post_quorum_timeout: Duration::from_secs(5),
312 serial_authority_request_interval: Duration::from_secs(1),
313 };
314 AuthorityAggregatorBuilder::from_genesis(genesis)
315 .with_timeouts_config(timeouts)
316 .build_custom_clients(clients)
317}
318
319pub fn make_transfer_iota_transaction(
320 gas_object: ObjectRef,
321 recipient: IotaAddress,
322 amount: Option<u64>,
323 sender: IotaAddress,
324 keypair: &AccountKeyPair,
325 gas_price: u64,
326) -> Transaction {
327 let data = TransactionData::new_transfer_iota(
328 recipient,
329 sender,
330 amount,
331 gas_object,
332 gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
333 gas_price,
334 );
335 to_sender_signed_transaction(data, keypair)
336}
337
338pub fn make_pay_iota_transaction(
339 gas_object: ObjectRef,
340 coins: Vec<ObjectRef>,
341 recipients: Vec<IotaAddress>,
342 amounts: Vec<u64>,
343 sender: IotaAddress,
344 keypair: &AccountKeyPair,
345 gas_price: u64,
346 gas_budget: u64,
347) -> Transaction {
348 let data = TransactionData::new_pay_iota(
349 sender, coins, recipients, amounts, gas_object, gas_budget, gas_price,
350 )
351 .unwrap();
352 to_sender_signed_transaction(data, keypair)
353}
354
355pub fn make_transfer_object_transaction(
356 object_ref: ObjectRef,
357 gas_object: ObjectRef,
358 sender: IotaAddress,
359 keypair: &AccountKeyPair,
360 recipient: IotaAddress,
361 gas_price: u64,
362) -> Transaction {
363 let data = TransactionData::new_transfer(
364 recipient,
365 object_ref,
366 sender,
367 gas_object,
368 gas_price * TEST_ONLY_GAS_UNIT_FOR_TRANSFER * 10,
369 gas_price,
370 );
371 to_sender_signed_transaction(data, keypair)
372}
373
374pub fn make_transfer_object_move_transaction(
375 src: IotaAddress,
376 keypair: &AccountKeyPair,
377 dest: IotaAddress,
378 object_ref: ObjectRef,
379 framework_obj_id: ObjectID,
380 gas_object_ref: ObjectRef,
381 gas_budget_in_units: u64,
382 gas_price: u64,
383) -> Transaction {
384 let args = vec![
385 CallArg::Object(ObjectArg::ImmOrOwnedObject(object_ref)),
386 CallArg::Pure(bcs::to_bytes(&AccountAddress::from(dest)).unwrap()),
387 ];
388
389 to_sender_signed_transaction(
390 TransactionData::new_move_call(
391 src,
392 framework_obj_id,
393 ident_str!("object_basics").to_owned(),
394 ident_str!("transfer").to_owned(),
395 Vec::new(),
396 gas_object_ref,
397 args,
398 gas_budget_in_units * gas_price,
399 gas_price,
400 )
401 .unwrap(),
402 keypair,
403 )
404}
405
406pub fn make_dummy_tx(
408 receiver: IotaAddress,
409 sender: IotaAddress,
410 sender_sec: &AccountKeyPair,
411) -> Transaction {
412 Transaction::from_data_and_signer(
413 TransactionData::new_transfer(
414 receiver,
415 random_object_ref(),
416 sender,
417 random_object_ref(),
418 TEST_ONLY_GAS_UNIT_FOR_TRANSFER * 10,
419 10,
420 ),
421 vec![sender_sec],
422 )
423}
424
425pub fn make_cert_with_large_committee(
427 committee: &Committee,
428 key_pairs: &[AuthorityKeyPair],
429 transaction: &Transaction,
430) -> CertifiedTransaction {
431 let len = committee.voting_rights.len();
433 assert_eq!(len, key_pairs.len());
434 let count = (len * 2).div_ceil(3);
435
436 let sigs: Vec<_> = key_pairs
437 .iter()
438 .take(count)
439 .map(|key_pair| {
440 SignedTransaction::new(
441 committee.epoch(),
442 transaction.clone().into_data(),
443 key_pair,
444 AuthorityPublicKeyBytes::from(key_pair.public()),
445 )
446 .auth_sig()
447 .clone()
448 })
449 .collect();
450
451 let cert = CertifiedTransaction::new(transaction.clone().into_data(), sigs, committee).unwrap();
452 cert.verify_signatures_authenticated(
453 committee,
454 &Default::default(),
455 Arc::new(VerifiedDigestCache::new_empty()),
456 )
457 .unwrap();
458 cert
459}