iota_types/unit_tests/
utils.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::BTreeMap;
6
7use fastcrypto::traits::KeyPair as KeypairTraits;
8use iota_sdk_types::crypto::{Intent, IntentMessage};
9use rand::{SeedableRng, rngs::StdRng};
10
11use crate::{
12    IotaAddress,
13    base_types::{ObjectID, dbg_addr},
14    committee::Committee,
15    crypto::{
16        AccountKeyPair, AuthorityKeyPair, AuthorityPublicKeyBytes, IotaKeyPair, Signature, Signer,
17        get_key_pair, get_key_pair_from_rng,
18    },
19    multisig::{MultiSig, MultiSigPublicKey},
20    object::Object,
21    programmable_transaction_builder::ProgrammableTransactionBuilder,
22    signature::GenericSignature,
23    transaction::{
24        SenderSignedData, TEST_ONLY_GAS_UNIT_FOR_TRANSFER, Transaction, TransactionData,
25    },
26};
27
28pub fn make_committee_key<R>(rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
29where
30    R: rand::CryptoRng + rand::RngCore,
31{
32    make_committee_key_num(4, rand)
33}
34
35pub fn make_committee_key_num<R>(num: usize, rand: &mut R) -> (Vec<AuthorityKeyPair>, Committee)
36where
37    R: rand::CryptoRng + rand::RngCore,
38{
39    let mut authorities: BTreeMap<AuthorityPublicKeyBytes, u64> = BTreeMap::new();
40    let mut keys = Vec::new();
41
42    for _ in 0..num {
43        let (_, inner_authority_key): (_, AuthorityKeyPair) = get_key_pair_from_rng(rand);
44        authorities.insert(
45            // address
46            AuthorityPublicKeyBytes::from(inner_authority_key.public()),
47            // voting right
48            1,
49        );
50        keys.push(inner_authority_key);
51    }
52
53    let committee = Committee::new_for_testing_with_normalized_voting_power(0, authorities);
54    (keys, committee)
55}
56
57// Creates a fake sender-signed transaction for testing. This transaction will
58// not actually work.
59pub fn create_fake_transaction() -> Transaction {
60    let (sender, sender_key): (_, AccountKeyPair) = get_key_pair();
61    let recipient = dbg_addr(2);
62    let object_id = ObjectID::random();
63    let object = Object::immutable_with_id_for_testing(object_id);
64    let pt = {
65        let mut builder = ProgrammableTransactionBuilder::new();
66        builder.transfer_iota(recipient, None);
67        builder.finish()
68    };
69    let data = TransactionData::new_programmable(
70        sender,
71        vec![object.compute_object_reference()],
72        pt,
73        TEST_ONLY_GAS_UNIT_FOR_TRANSFER, // gas price is 1
74        1,
75    );
76    to_sender_signed_transaction(data, &sender_key)
77}
78
79pub fn make_transaction_data(sender: IotaAddress) -> TransactionData {
80    let object = Object::immutable_with_id_for_testing(ObjectID::random_from_rng(
81        &mut StdRng::from_seed([0; 32]),
82    ));
83    let pt = {
84        let mut builder = ProgrammableTransactionBuilder::new();
85        builder.transfer_iota(dbg_addr(2), None);
86        builder.finish()
87    };
88    TransactionData::new_programmable(
89        sender,
90        vec![object.compute_object_reference()],
91        pt,
92        TEST_ONLY_GAS_UNIT_FOR_TRANSFER, // gas price is 1
93        1,
94    )
95}
96
97/// Make a user signed transaction with the given sender and its keypair. This
98/// is not verified or signed by authority.
99pub fn make_transaction(sender: IotaAddress, kp: &IotaKeyPair) -> Transaction {
100    let data = make_transaction_data(sender);
101    Transaction::from_data_and_signer(data, vec![kp])
102}
103
104// This is used to sign transaction with signer using default Intent.
105pub fn to_sender_signed_transaction(
106    data: TransactionData,
107    signer: &dyn Signer<Signature>,
108) -> Transaction {
109    to_sender_signed_transaction_with_multi_signers(data, vec![signer])
110}
111
112pub fn to_sender_signed_transaction_with_optional_sponsor(
113    data: TransactionData,
114    sender_signature: GenericSignature,
115    sponsor_signer_opt: Option<&dyn Signer<Signature>>,
116) -> Transaction {
117    let mut signatures = vec![sender_signature];
118    if let Some(sponsor) = sponsor_signer_opt {
119        let sponsor_sig =
120            Transaction::signature_from_signer(data.clone(), Intent::iota_transaction(), sponsor)
121                .into();
122        signatures.push(sponsor_sig);
123    };
124
125    Transaction::from_generic_sig_data(data, signatures)
126}
127
128pub fn to_sender_signed_transaction_with_multi_signers(
129    data: TransactionData,
130    signers: Vec<&dyn Signer<Signature>>,
131) -> Transaction {
132    Transaction::from_data_and_signer(data, signers)
133}
134
135pub fn keys() -> Vec<IotaKeyPair> {
136    let mut seed = StdRng::from_seed([0; 32]);
137    let kp1: IotaKeyPair = IotaKeyPair::Ed25519(get_key_pair_from_rng(&mut seed).1);
138    let kp2: IotaKeyPair = IotaKeyPair::Secp256k1(get_key_pair_from_rng(&mut seed).1);
139    let kp3: IotaKeyPair = IotaKeyPair::Secp256r1(get_key_pair_from_rng(&mut seed).1);
140    vec![kp1, kp2, kp3]
141}
142
143pub fn make_upgraded_multisig_tx() -> Transaction {
144    let keys = keys();
145    let pk1 = &keys[0].public();
146    let pk2 = &keys[1].public();
147    let pk3 = &keys[2].public();
148
149    let multisig_pk = MultiSigPublicKey::new(
150        vec![pk1.clone(), pk2.clone(), pk3.clone()],
151        vec![1, 1, 1],
152        2,
153    )
154    .unwrap();
155    let addr = IotaAddress::from(&multisig_pk);
156    let tx = make_transaction(addr, &keys[0]);
157
158    let msg = IntentMessage::new(Intent::iota_transaction(), tx.transaction_data().clone());
159    let sig1 = Signature::new_secure(&msg, &keys[0]).into();
160    let sig2 = Signature::new_secure(&msg, &keys[1]).into();
161
162    // Any 2 of 3 signatures verifies ok.
163    let multi_sig1 = MultiSig::combine(vec![sig1, sig2], multisig_pk).unwrap();
164    Transaction::new(SenderSignedData::new(
165        tx.transaction_data().clone(),
166        vec![GenericSignature::MultiSig(multi_sig1)],
167    ))
168}
169
170mod move_authenticator {
171    pub use crate::move_authenticator::MoveAuthenticator;
172    use crate::{
173        base_types::IotaAddress,
174        object::OBJECT_START_VERSION,
175        signature::GenericSignature,
176        transaction::{CallArg, ObjectArg, SenderSignedData, Transaction},
177        utils::make_transaction_data,
178    };
179
180    /// Make a transaction signed with `MoveAuthenticator` for testing.
181    pub fn make_move_authenticator_tx(address: IotaAddress) -> Transaction {
182        let data = make_transaction_data(address);
183
184        // There is no a real Move account behind this address.
185        //
186        // TODO: if it is necessary, AA accounts need to be supported properly in the
187        // `AuthorityState` used for testing.
188        let self_call_arg = CallArg::Object(ObjectArg::SharedObject {
189            id: address.into(),
190            initial_shared_version: OBJECT_START_VERSION,
191            mutable: false,
192        });
193        let authenticator = GenericSignature::MoveAuthenticator(MoveAuthenticator::new_v1(
194            vec![],
195            vec![],
196            self_call_arg,
197        ));
198
199        Transaction::new(SenderSignedData::new(data, vec![authenticator]))
200    }
201}
202
203pub use move_authenticator::*;