1use std::collections::BTreeMap;
6
7use fastcrypto::traits::KeyPair as KeypairTraits;
8use iota_protocol_config::ProtocolConfig;
9use iota_sdk_crypto::{
10 Signer, ed25519::Ed25519PrivateKey, secp256k1::Secp256k1PrivateKey,
11 secp256r1::Secp256r1PrivateKey, simple::SimpleKeypair,
12};
13use iota_sdk_types::{
14 Address, Argument, Command, ObjectId, ProgrammableTransaction, SenderSignedTransaction,
15 SharedObjectReference, SimpleSignature, Transaction, TransactionKind, TransferObjects,
16 UserSignature, Version,
17 crypto::{Intent, MultisigAggregatedSignature, MultisigCommittee, MultisigMember},
18};
19use rand::{SeedableRng, rngs::StdRng};
20
21use crate::{
22 base_types::{dbg_addr, random_object_ref},
23 committee::Committee,
24 crypto::{
25 AccountPrivateKey, AuthorityKeyPair, AuthorityPublicKeyBytes, get_key_pair,
26 get_key_pair_from_rng,
27 },
28 error::UserInputError,
29 object::Object,
30 programmable_transaction_builder::ProgrammableTransactionBuilder,
31 transaction::{CallArg, TEST_ONLY_GAS_UNIT_FOR_TRANSFER, TransactionAPI, TransactionEnvelope},
32};
33
34pub fn make_committee_key<R>(rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
35where
36 R: rand::CryptoRng,
37{
38 make_committee_key_num(4, rand)
39}
40
41pub fn make_committee_key_num<R>(num: usize, rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
42where
43 R: rand::CryptoRng,
44{
45 let mut authorities: BTreeMap<AuthorityPublicKeyBytes, u64> = BTreeMap::new();
46 let mut keys = Vec::new();
47
48 for _ in 0..num {
49 let (_, inner_authority_key): (_, AuthorityKeyPair) = get_key_pair_from_rng(rand);
50 authorities.insert(
51 AuthorityPublicKeyBytes::from(inner_authority_key.public()),
53 1,
55 );
56 keys.push(inner_authority_key);
57 }
58
59 let committee = Committee::new_for_testing_with_normalized_voting_power(0, authorities);
60 (keys, committee)
61}
62
63pub fn create_fake_transaction() -> TransactionEnvelope {
66 let (sender, sender_key): (_, AccountPrivateKey) = get_key_pair();
67 let recipient = dbg_addr(2);
68 let object_id = ObjectId::random();
69 let object = Object::immutable_with_id_for_testing(object_id);
70 let pt = {
71 let mut builder = ProgrammableTransactionBuilder::new();
72 builder.transfer_iota(recipient, None);
73 builder.finish()
74 };
75 let tx = Transaction::new_programmable(
76 sender,
77 vec![object.object_ref()],
78 pt,
79 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, 1,
81 );
82 to_sender_signed_transaction(tx, &sender_key)
83}
84
85pub fn make_transaction_data(sender: Address) -> Transaction {
86 let object =
87 Object::immutable_with_id_for_testing(ObjectId::random_with(StdRng::from_seed([0; 32])));
88 let pt = {
89 let mut builder = ProgrammableTransactionBuilder::new();
90 builder.transfer_iota(dbg_addr(2), None);
91 builder.finish()
92 };
93 Transaction::new_programmable(
94 sender,
95 vec![object.object_ref()],
96 pt,
97 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, 1,
99 )
100}
101
102pub fn make_sponsored_transaction_data(sender: Address, sponsor: Address) -> Transaction {
105 let pt = {
106 let mut builder = ProgrammableTransactionBuilder::new();
107 builder.transfer_iota(dbg_addr(2), None);
108 builder.finish()
109 };
110 Transaction::new_with_gas_coins_allow_sponsor(
111 TransactionKind::new_programmable(pt),
112 sender,
113 vec![random_object_ref()],
114 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, 1,
116 sponsor,
117 )
118}
119
120pub fn make_transaction(sender: Address, kp: &SimpleKeypair) -> TransactionEnvelope {
123 let data = make_transaction_data(sender);
124 TransactionEnvelope::from_data_and_signer(data, vec![kp])
125}
126
127pub fn to_sender_signed_transaction(
129 tx: Transaction,
130 signer: &impl Signer<SimpleSignature>,
131) -> TransactionEnvelope {
132 to_sender_signed_transaction_with_multi_signers(tx, vec![signer])
133}
134
135pub fn to_sender_signed_transaction_with_optional_sponsor(
136 tx: Transaction,
137 sender_signature: UserSignature,
138 sponsor_signer_opt: Option<&impl Signer<SimpleSignature>>,
139) -> TransactionEnvelope {
140 let mut signatures = vec![sender_signature];
141 if let Some(sponsor) = sponsor_signer_opt {
142 let sponsor_sig = TransactionEnvelope::signature_from_signer(
143 tx.clone(),
144 Intent::iota_transaction(),
145 sponsor,
146 )
147 .into();
148 signatures.push(sponsor_sig);
149 };
150
151 TransactionEnvelope::from_user_sig_data(tx, signatures)
152}
153
154pub fn to_sender_signed_transaction_with_multi_signers(
155 tx: Transaction,
156 signers: Vec<&impl Signer<SimpleSignature>>,
157) -> TransactionEnvelope {
158 TransactionEnvelope::from_data_and_signer(tx, signers)
159}
160
161pub fn make_upgraded_multisig_tx() -> TransactionEnvelope {
162 let kp1 = Ed25519PrivateKey::random();
163 let kp2 = Secp256k1PrivateKey::random();
164 let kp3 = Secp256r1PrivateKey::random();
165 let pk1 = kp1.public_key();
166 let pk2 = kp2.public_key();
167 let pk3 = kp3.public_key();
168
169 let multisig_pk = MultisigCommittee::new_unchecked(
173 vec![
174 MultisigMember::new(pk1, 1),
175 MultisigMember::new(pk2, 1),
176 MultisigMember::new(pk3, 1),
177 ],
178 2,
179 );
180 let addr = Address::from(&multisig_pk);
181 let tx = make_transaction(addr, &SimpleKeypair::from(kp1.clone()));
182
183 let msg = tx.transaction().signing_digest();
184 let sig1: SimpleSignature = kp1.sign(&msg);
185 let sig2: SimpleSignature = kp2.sign(&msg);
186 let sig3: SimpleSignature = kp3.sign(&msg);
187
188 let multi_sig1 =
189 MultisigAggregatedSignature::new(vec![sig1.into(), sig2.into(), sig3.into()], multisig_pk)
190 .unwrap();
191 TransactionEnvelope::new(SenderSignedTransaction::new(
192 tx.transaction().clone(),
193 vec![UserSignature::Multisig(multi_sig1)],
194 ))
195}
196
197pub fn make_sponsored_regular_sig_tx() -> (TransactionEnvelope, Address, Address) {
203 let (sender, sender_key): (_, AccountPrivateKey) = get_key_pair();
204 let (sponsor, sponsor_key): (_, AccountPrivateKey) = get_key_pair();
205 let tx_data = make_sponsored_transaction_data(sender, sponsor);
206 let sender_sig: UserSignature = TransactionEnvelope::signature_from_signer(
207 tx_data.clone(),
208 Intent::iota_transaction(),
209 &sender_key,
210 )
211 .into();
212 let tx =
213 to_sender_signed_transaction_with_optional_sponsor(tx_data, sender_sig, Some(&sponsor_key));
214 (tx, sender, sponsor)
215}
216
217mod move_authenticator {
218 use fastcrypto::hash::HashFunction;
219 use iota_sdk_types::{
220 Address, Digest, Identifier, MoveAuthenticator, MoveAuthenticatorV1, ObjectId,
221 SenderSignedTransaction, SharedObjectReference, Transaction, TransactionExpiration,
222 UserSignature, Version,
223 };
224
225 use crate::{
226 base_types::random_object_ref,
227 committee::EpochId,
228 crypto::DefaultHash,
229 object::OBJECT_START_VERSION,
230 programmable_transaction_builder::ProgrammableTransactionBuilder,
231 transaction::{
232 CallArg, TEST_ONLY_GAS_UNIT_FOR_TRANSFER, TransactionAPI, TransactionEnvelope,
233 },
234 utils::{make_sponsored_transaction_data, make_transaction_data},
235 };
236
237 pub fn make_move_authenticator_tx(address: Address) -> TransactionEnvelope {
239 let data = make_transaction_data(address);
240 let (authenticator, _) = make_move_authenticator_sig(address);
241 TransactionEnvelope::new(SenderSignedTransaction::new(data, vec![authenticator]))
242 }
243
244 pub fn make_move_authenticator_sig(address: Address) -> (UserSignature, MoveAuthenticator) {
252 let authenticator = MoveAuthenticator::from(MoveAuthenticatorV1::new(
253 vec![],
254 vec![],
255 SharedObjectReference::new(address.into(), OBJECT_START_VERSION, false),
256 ));
257 let sig = UserSignature::MoveAuthenticator(authenticator.clone());
258 (sig, authenticator)
259 }
260
261 pub fn make_move_authenticator_tx_with_inconsistent_shared_input(
269 address: Address,
270 epoch: EpochId,
271 ) -> (TransactionEnvelope, SharedObjectReference) {
272 let shared = SharedObjectReference::new(ObjectId::random(), Version::from(1), false);
273 let mut builder = ProgrammableTransactionBuilder::new();
274 let arg = builder.obj(CallArg::Shared(shared)).unwrap();
275 builder.programmable_move_call(
276 ObjectId::random(),
277 Identifier::from_static("module"),
278 Identifier::from_static("function"),
279 vec![],
280 vec![arg],
281 );
282 let mut data = Transaction::new_programmable(
283 address,
284 vec![random_object_ref()],
285 builder.finish(),
286 TEST_ONLY_GAS_UNIT_FOR_TRANSFER,
287 1,
288 );
289 *data.expiration_mut_for_testing() = TransactionExpiration::Epoch(epoch);
290 let authenticator = MoveAuthenticator::from(MoveAuthenticatorV1::new(
291 vec![CallArg::Shared(SharedObjectReference::new(
292 shared.object_id,
293 Version::from(2),
294 false,
295 ))],
296 vec![],
297 SharedObjectReference::new(address.into(), OBJECT_START_VERSION, false),
298 ));
299 let tx = TransactionEnvelope::new(SenderSignedTransaction::new(
300 data,
301 vec![UserSignature::MoveAuthenticator(authenticator)],
302 ));
303 (tx, shared)
304 }
305
306 pub fn make_sponsored_move_authenticator_tx(
313 sender_addr: Address,
314 sponsor_addr: Address,
315 ) -> (TransactionEnvelope, MoveAuthenticator, MoveAuthenticator) {
316 let (sender_sig, sender_auth) = make_move_authenticator_sig(sender_addr);
317 let (sponsor_sig, sponsor_auth) = make_move_authenticator_sig(sponsor_addr);
318 let tx_data = make_sponsored_transaction_data(sender_addr, sponsor_addr);
319 let tx = TransactionEnvelope::new(SenderSignedTransaction::new(
320 tx_data,
321 vec![sender_sig, sponsor_sig],
322 ));
323 (tx, sender_auth, sponsor_auth)
324 }
325
326 pub fn blake2b256_of_sig(sig: &UserSignature) -> Digest {
331 let mut hasher = DefaultHash::default();
332 hasher.update(sig.to_bytes());
333 Digest::new(hasher.finalize().into())
334 }
335}
336
337pub use move_authenticator::*;
338
339mod passkey {
340 use iota_sdk_crypto::secp256r1::Secp256r1PrivateKey;
341 use iota_sdk_types::crypto::PasskeyAuthenticator;
342
343 use super::*;
344
345 pub fn make_passkey_authenticator_sig() -> UserSignature {
352 let r1_kp = Secp256r1PrivateKey::random();
353 let user_sig: SimpleSignature = r1_kp.sign(&[0u8; 32]);
354 let client_data_json = r#"{"type":"webauthn.get","challenge":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","origin":"https://test.iota.org"}"#;
355 let passkey =
356 PasskeyAuthenticator::new(vec![], client_data_json.to_string(), user_sig).unwrap();
357 UserSignature::PasskeyAuthenticator(passkey)
358 }
359}
360
361pub use passkey::*;
362
363#[track_caller]
366pub fn assert_size_limit_err(err: &UserInputError, limit_name: &str) {
367 let UserInputError::SizeLimitExceeded { limit, .. } = err else {
368 panic!("expected a size limit, got {err:?}");
369 };
370 assert!(
371 limit == limit_name || limit.starts_with(&format!("{limit_name} of ")),
372 "expected {limit_name}, got {limit}"
373 );
374}
375
376pub fn ptb_with_pure_inputs(
380 count: usize,
381 size: usize,
382 with_randomness: bool,
383) -> ProgrammableTransaction {
384 let mut inputs = vec![CallArg::Pure(vec![0; size]); count];
385 if with_randomness {
386 inputs.push(CallArg::Shared(SharedObjectReference {
387 object_id: ObjectId::RANDOMNESS_STATE,
388 initial_shared_version: Version::from_u64(1),
389 mutable: false,
390 }));
391 }
392 assert!(inputs.len() >= 2, "the command names the first two inputs");
393 let commands = vec![Command::TransferObjects(TransferObjects {
394 objects: vec![Argument::Input(0)],
395 address: Argument::Input(1),
396 })];
397 ProgrammableTransaction { inputs, commands }
398}
399
400pub fn ptb_above_max_tx_size(config: &ProtocolConfig) -> ProgrammableTransaction {
404 let pure_size = config.max_pure_argument_size() as usize - 1;
405 let count = config.max_tx_size_bytes() as usize / pure_size + 1;
406 ptb_with_pure_inputs(count, pure_size, false)
407}