1use std::collections::BTreeMap;
6
7use fastcrypto::{ed25519::Ed25519KeyPair, hash::HashFunction, traits::KeyPair as KeypairTraits};
8use iota_sdk_types::crypto::{Intent, IntentMessage};
9use rand::{SeedableRng, rngs::StdRng};
10use serde::Deserialize;
11
12use crate::{
13 IotaAddress,
14 base_types::{ObjectID, dbg_addr},
15 committee::Committee,
16 crypto::{
17 AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, DefaultHash, IotaKeyPair,
18 Signature, SignatureScheme, Signer, get_key_pair, get_key_pair_from_rng,
19 },
20 multisig::{MultiSig, MultiSigPublicKey},
21 object::Object,
22 programmable_transaction_builder::ProgrammableTransactionBuilder,
23 signature::GenericSignature,
24 transaction::{
25 SenderSignedData, TEST_ONLY_GAS_UNIT_FOR_TRANSFER, Transaction, TransactionData,
26 },
27 zk_login_authenticator::ZkLoginAuthenticator,
28};
29
30#[derive(Deserialize)]
31pub struct TestData {
32 pub zklogin_inputs: String,
33 pub kp: String,
34 pub pk_bigint: String,
35 pub salt: String,
36 pub address_seed: String,
37}
38
39pub fn make_committee_key<R>(rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
40where
41 R: rand::CryptoRng + rand::RngCore,
42{
43 make_committee_key_num(4, rand)
44}
45
46pub fn make_committee_key_num<R>(num: usize, rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
47where
48 R: rand::CryptoRng + rand::RngCore,
49{
50 let mut authorities: BTreeMap<AuthorityPublicKeyBytes, u64> = BTreeMap::new();
51 let mut keys = Vec::new();
52
53 for _ in 0..num {
54 let (_, inner_authority_key): (_, AuthorityKeyPair) = get_key_pair_from_rng(rand);
55 authorities.insert(
56 AuthorityPublicKeyBytes::from(inner_authority_key.public()),
58 1,
60 );
61 keys.push(inner_authority_key);
62 }
63
64 let committee = Committee::new_for_testing_with_normalized_voting_power(0, authorities);
65 (keys, committee)
66}
67
68pub fn create_fake_transaction() -> Transaction {
71 let (sender, sender_key): (_, AccountKeyPair) = get_key_pair();
72 let recipient = dbg_addr(2);
73 let object_id = ObjectID::random();
74 let object = Object::immutable_with_id_for_testing(object_id);
75 let pt = {
76 let mut builder = ProgrammableTransactionBuilder::new();
77 builder.transfer_iota(recipient, None);
78 builder.finish()
79 };
80 let data = TransactionData::new_programmable(
81 sender,
82 vec![object.compute_object_reference()],
83 pt,
84 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, 1,
86 );
87 to_sender_signed_transaction(data, &sender_key)
88}
89
90pub fn make_transaction_data(sender: IotaAddress) -> TransactionData {
91 let object = Object::immutable_with_id_for_testing(ObjectID::random_from_rng(
92 &mut StdRng::from_seed([0; 32]),
93 ));
94 let pt = {
95 let mut builder = ProgrammableTransactionBuilder::new();
96 builder.transfer_iota(dbg_addr(2), None);
97 builder.finish()
98 };
99 TransactionData::new_programmable(
100 sender,
101 vec![object.compute_object_reference()],
102 pt,
103 TEST_ONLY_GAS_UNIT_FOR_TRANSFER, 1,
105 )
106}
107
108pub fn make_transaction(sender: IotaAddress, kp: &IotaKeyPair) -> Transaction {
111 let data = make_transaction_data(sender);
112 Transaction::from_data_and_signer(data, vec![kp])
113}
114
115pub fn to_sender_signed_transaction(
117 data: TransactionData,
118 signer: &dyn Signer<Signature>,
119) -> Transaction {
120 to_sender_signed_transaction_with_multi_signers(data, vec![signer])
121}
122
123pub fn to_sender_signed_transaction_with_optional_sponsor(
124 data: TransactionData,
125 sender_signature: GenericSignature,
126 sponsor_signer_opt: Option<&dyn Signer<Signature>>,
127) -> Transaction {
128 let mut signatures = vec![sender_signature];
129 if let Some(sponsor) = sponsor_signer_opt {
130 let sponsor_sig =
131 Transaction::signature_from_signer(data.clone(), Intent::iota_transaction(), sponsor)
132 .into();
133 signatures.push(sponsor_sig);
134 };
135
136 Transaction::from_generic_sig_data(data, signatures)
137}
138
139pub fn to_sender_signed_transaction_with_multi_signers(
140 data: TransactionData,
141 signers: Vec<&dyn Signer<Signature>>,
142) -> Transaction {
143 Transaction::from_data_and_signer(data, signers)
144}
145
146mod zk_login {
147 use fastcrypto_zkp::bn254::zk_login::ZkLoginInputs;
148 use iota_sdk_types::crypto::PersonalMessage;
149
150 use super::*;
151 use crate::{crypto::PublicKey, zk_login_util::get_zklogin_inputs};
152 pub static DEFAULT_ADDRESS_SEED: &str =
153 "20794788559620669596206457022966176986688727876128223628113916380927502737911";
154 pub static SHORT_ADDRESS_SEED: &str =
155 "380704556853533152350240698167704405529973457670972223618755249929828551006";
156
157 pub fn load_test_vectors(
158 path: &str,
159 ) -> eyre::Result<Vec<(IotaKeyPair, PublicKey, ZkLoginInputs)>> {
160 let file = std::fs::File::open(path).expect("Unable to open file");
163
164 let test_datum: Vec<TestData> = serde_json::from_reader(file)?;
165 let mut res = vec![];
166 for test in test_datum {
167 let kp = IotaKeyPair::decode(&test.kp)?;
168 let inputs = ZkLoginInputs::from_json(&test.zklogin_inputs, &test.address_seed)?;
169 let pk_zklogin = PublicKey::from_zklogin_inputs(&inputs)?;
170 res.push((kp, pk_zklogin, inputs));
171 }
172 Ok(res)
173 }
174 pub fn get_one_zklogin_inputs(path: &str) -> String {
175 let file = std::fs::File::open(path).expect("Unable to open file");
176
177 let test_data: Vec<TestData> = serde_json::from_reader(file).unwrap();
178 test_data[1].zklogin_inputs.clone()
179 }
180
181 pub fn get_zklogin_user_address() -> IotaAddress {
182 thread_local! {
183 static USER_ADDRESS: IotaAddress = {
184 let mut hasher = DefaultHash::default();
186 hasher.update([SignatureScheme::ZkLoginAuthenticator.flag()]);
187 let inputs = get_zklogin_inputs();
188 let iss_bytes = inputs.get_iss().as_bytes();
189 hasher.update([iss_bytes.len() as u8]);
190 hasher.update(iss_bytes);
191 hasher.update(inputs.get_address_seed().unpadded());
192 IotaAddress::from_bytes(hasher.finalize().digest).unwrap()
193 };
194 }
195 USER_ADDRESS.with(|a| *a)
196 }
197
198 fn get_zklogin_user_key() -> IotaKeyPair {
199 IotaKeyPair::Ed25519(Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32])))
200 }
201
202 fn get_inputs_with_bad_address_seed() -> ZkLoginInputs {
203 thread_local! {
204 static ZKLOGIN_INPUTS: ZkLoginInputs = ZkLoginInputs::from_json("{\"proofPoints\":{\"a\":[\"17276311605393076686048412951904952585208929623427027497902331765285829154985\",\"2195957390349729412627479867125563520760023859523358729791332629632025124364\",\"1\"],\"b\":[[\"10285059021604767951039627893758482248204478992077021270802057708215366770814\",\"20086937595807139308592304218494658586282197458549968652049579308384943311509\"],[\"7481123765095657256931104563876569626157448050870256177668773471703520958615\",\"11912752790863530118410797223176516777328266521602785233083571774104055633375\"],[\"1\",\"0\"]],\"c\":[\"15742763887654796666500488588763616323599882100448686869458326409877111249163\",\"6112916537574993759490787691149241262893771114597679488354854987586060572876\",\"1\"]},\"issBase64Details\":{\"value\":\"wiaXNzIjoiaHR0cHM6Ly9pZC50d2l0Y2gudHYvb2F1dGgyIiw\",\"indexMod4\":2},\"headerBase64\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEifQ\"}", SHORT_ADDRESS_SEED).unwrap(); }
205 ZKLOGIN_INPUTS.with(|a| a.clone())
206 }
207
208 pub fn get_legacy_zklogin_user_address() -> IotaAddress {
209 thread_local! {
210 static USER_ADDRESS: IotaAddress = {
211 let inputs = get_inputs_with_bad_address_seed();
212 IotaAddress::from(&PublicKey::from_zklogin_inputs(&inputs).unwrap())
213 };
214 }
215 USER_ADDRESS.with(|a| *a)
216 }
217
218 pub fn sign_zklogin_personal_msg(data: PersonalMessage<'_>) -> (IotaAddress, GenericSignature) {
219 let inputs = get_zklogin_inputs();
220 let msg = IntentMessage::new(Intent::personal_message(), data.0);
221 let s = Signature::new_secure(&msg, &get_zklogin_user_key());
222 let authenticator =
223 GenericSignature::ZkLoginAuthenticator(ZkLoginAuthenticator::new(inputs, 10, s));
224 let address = get_zklogin_user_address();
225 (address, authenticator)
226 }
227
228 pub fn sign_zklogin_tx_with_default_proof(
229 data: TransactionData,
230 legacy: bool,
231 ) -> (IotaAddress, Transaction, GenericSignature) {
232 let inputs = if legacy {
233 get_inputs_with_bad_address_seed()
234 } else {
235 get_zklogin_inputs()
236 };
237
238 sign_zklogin_tx(&get_zklogin_user_key(), inputs, data)
239 }
240
241 pub fn sign_zklogin_tx(
242 user_key: &IotaKeyPair,
243 proof: ZkLoginInputs,
244 data: TransactionData,
245 ) -> (IotaAddress, Transaction, GenericSignature) {
246 let tx = Transaction::from_data_and_signer(data.clone(), vec![user_key]);
247
248 let s = match tx.inner().tx_signatures.first().unwrap() {
249 GenericSignature::Signature(s) => s,
250 _ => panic!("Expected a signature"),
251 };
252
253 let authenticator =
255 GenericSignature::ZkLoginAuthenticator(ZkLoginAuthenticator::new(proof, 10, s.clone()));
256
257 let tx = Transaction::new(SenderSignedData::new(
258 tx.transaction_data().clone(),
259 vec![authenticator.clone()],
260 ));
261 (data.execution_parts().1, tx, authenticator)
262 }
263
264 pub fn make_zklogin_tx(
265 address: IotaAddress,
266 legacy: bool,
267 ) -> (IotaAddress, Transaction, GenericSignature) {
268 let data = make_transaction_data(address);
269 sign_zklogin_tx_with_default_proof(data, legacy)
270 }
271
272 pub fn keys() -> Vec<IotaKeyPair> {
273 let mut seed = StdRng::from_seed([0; 32]);
274 let kp1: IotaKeyPair = IotaKeyPair::Ed25519(get_key_pair_from_rng(&mut seed).1);
275 let kp2: IotaKeyPair = IotaKeyPair::Secp256k1(get_key_pair_from_rng(&mut seed).1);
276 let kp3: IotaKeyPair = IotaKeyPair::Secp256r1(get_key_pair_from_rng(&mut seed).1);
277 vec![kp1, kp2, kp3]
278 }
279
280 pub fn make_upgraded_multisig_tx() -> Transaction {
281 let keys = keys();
282 let pk1 = &keys[0].public();
283 let pk2 = &keys[1].public();
284 let pk3 = &keys[2].public();
285
286 let multisig_pk = MultiSigPublicKey::new(
287 vec![pk1.clone(), pk2.clone(), pk3.clone()],
288 vec![1, 1, 1],
289 2,
290 )
291 .unwrap();
292 let addr = IotaAddress::from(&multisig_pk);
293 let tx = make_transaction(addr, &keys[0]);
294
295 let msg = IntentMessage::new(Intent::iota_transaction(), tx.transaction_data().clone());
296 let sig1 = Signature::new_secure(&msg, &keys[0]).into();
297 let sig2 = Signature::new_secure(&msg, &keys[1]).into();
298
299 let multi_sig1 = MultiSig::combine(vec![sig1, sig2], multisig_pk).unwrap();
301 Transaction::new(SenderSignedData::new(
302 tx.transaction_data().clone(),
303 vec![GenericSignature::MultiSig(multi_sig1)],
304 ))
305 }
306}
307
308mod move_authenticator {
309 pub use crate::move_authenticator::MoveAuthenticator;
310 use crate::{
311 base_types::IotaAddress,
312 object::OBJECT_START_VERSION,
313 signature::GenericSignature,
314 transaction::{CallArg, ObjectArg, SenderSignedData, Transaction},
315 utils::make_transaction_data,
316 };
317
318 pub fn make_move_authenticator_tx(address: IotaAddress) -> Transaction {
320 let data = make_transaction_data(address);
321
322 let self_call_arg = CallArg::Object(ObjectArg::SharedObject {
327 id: address.into(),
328 initial_shared_version: OBJECT_START_VERSION,
329 mutable: false,
330 });
331 let authenticator = GenericSignature::MoveAuthenticator(MoveAuthenticator::new(
332 vec![],
333 vec![],
334 self_call_arg,
335 ));
336
337 Transaction::new(SenderSignedData::new(data, vec![authenticator]))
338 }
339}
340
341pub use move_authenticator::*;
342pub use zk_login::*;