transaction_fuzzer/
type_arg_fuzzer.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_core::test_utils::send_and_confirm_transaction;
6use iota_types::{
7    IOTA_FRAMEWORK_PACKAGE_ID, TypeTag,
8    base_types::ObjectID,
9    effects::{TransactionEffects, TransactionEffectsAPI},
10    error::IotaError,
11    programmable_transaction_builder::ProgrammableTransactionBuilder,
12    transaction::{ProgrammableTransaction, TransactionData, TransactionKind},
13    utils::to_sender_signed_transaction,
14};
15use move_core_types::{
16    account_address::AccountAddress, identifier::Identifier, language_storage::StructTag,
17};
18use proptest::{arbitrary::*, prelude::*};
19
20use crate::{
21    account_universe::AccountCurrent,
22    executor::{Executor, assert_is_acceptable_result},
23};
24
25const GAS_PRICE: u64 = 1000;
26const GAS: u64 = 1_000_000 * GAS_PRICE;
27
28pub fn gen_type_tag() -> impl Strategy<Value = TypeTag> {
29    prop_oneof![
30        2 => any::<TypeTag>(),
31        1 => gen_nested_type_tag()
32    ]
33}
34
35// Generate deep nested type tags
36pub fn gen_nested_type_tag() -> impl Strategy<Value = TypeTag> {
37    let leaf = prop_oneof![
38        Just(TypeTag::Bool),
39        Just(TypeTag::U8),
40        Just(TypeTag::U16),
41        Just(TypeTag::U32),
42        Just(TypeTag::U64),
43        Just(TypeTag::U128),
44        Just(TypeTag::U256),
45        Just(TypeTag::Address),
46        Just(TypeTag::Signer),
47    ];
48    leaf.prop_recursive(8, 6, 10, |inner| {
49        prop_oneof![
50            inner.prop_map(|x| TypeTag::Vector(Box::new(x))),
51            gen_struct_tag().prop_map(|x| TypeTag::Struct(Box::new(x))),
52        ]
53    })
54}
55
56pub fn gen_struct_tag() -> impl Strategy<Value = StructTag> {
57    (
58        any::<AccountAddress>(),
59        any::<Identifier>(),
60        any::<Identifier>(),
61        any::<Vec<TypeTag>>(),
62    )
63        .prop_map(|(address, module, name, type_params)| StructTag {
64            address,
65            module,
66            name,
67            type_params,
68        })
69}
70
71pub fn generate_valid_type_factory_tags(
72    type_factory_addr: ObjectID,
73) -> impl Strategy<Value = TypeTag> {
74    let leaf = prop_oneof![
75        base_type_factory_tag_gen(type_factory_addr),
76        nested_type_factory_tag_gen(type_factory_addr),
77    ];
78
79    leaf.prop_recursive(8, 6, 10, move |inner| {
80        prop_oneof![inner.prop_map(|x| TypeTag::Vector(Box::new(x))),]
81    })
82}
83
84pub fn generate_valid_and_invalid_type_factory_tags(
85    type_factory_addr: ObjectID,
86) -> impl Strategy<Value = TypeTag> {
87    let leaf = prop_oneof![
88        any::<TypeTag>(),
89        base_type_factory_tag_gen(type_factory_addr),
90        nested_type_factory_tag_gen(type_factory_addr),
91    ];
92
93    leaf.prop_recursive(8, 6, 10, move |inner| {
94        prop_oneof![inner.prop_map(|x| TypeTag::Vector(Box::new(x))),]
95    })
96}
97
98pub fn base_type_factory_tag_gen(addr: ObjectID) -> impl Strategy<Value = TypeTag> {
99    "[A-Z]".prop_map(move |name| {
100        TypeTag::Struct(Box::new(StructTag {
101            address: AccountAddress::from(addr),
102            module: Identifier::new("type_factory").unwrap(),
103            name: Identifier::new(name).unwrap(),
104            type_params: vec![],
105        }))
106    })
107}
108
109pub fn nested_type_factory_tag_gen(addr: ObjectID) -> impl Strategy<Value = TypeTag> {
110    base_type_factory_tag_gen(addr).prop_recursive(20, 256, 10, move |inner| {
111        (inner, "[A-Z]").prop_map(move |(instantiation, name)| {
112            TypeTag::Struct(Box::new(StructTag {
113                address: AccountAddress::from(addr),
114                module: Identifier::new("type_factory").unwrap(),
115                name: Identifier::new(name.to_string() + &name).unwrap(),
116                type_params: vec![instantiation],
117            }))
118        })
119    })
120}
121
122pub fn type_factory_pt_for_tags(
123    package_id: ObjectID,
124    type_tags: Vec<TypeTag>,
125    len: usize,
126) -> ProgrammableTransaction {
127    let mut builder = ProgrammableTransactionBuilder::new();
128    builder
129        .move_call(
130            package_id,
131            Identifier::new("type_factory").unwrap(),
132            Identifier::new(format!("type_tags{}", len)).unwrap(),
133            type_tags,
134            vec![],
135        )
136        .unwrap();
137    builder.finish()
138}
139
140pub fn pt_for_tags(type_tags: Vec<TypeTag>) -> ProgrammableTransaction {
141    let mut builder = ProgrammableTransactionBuilder::new();
142    builder
143        .move_call(
144            IOTA_FRAMEWORK_PACKAGE_ID,
145            Identifier::new("random_type_tag_fuzzing").unwrap(),
146            Identifier::new("random_type_tag_fuzzing_fn").unwrap(),
147            type_tags,
148            vec![],
149        )
150        .unwrap();
151    builder.finish()
152}
153
154pub fn run_pt(account: &mut AccountCurrent, exec: &mut Executor, pt: ProgrammableTransaction) {
155    let result = run_pt_effects(account, exec, pt);
156    let status = result.map(|effects| effects.status().clone());
157    assert_is_acceptable_result(&status);
158}
159
160pub fn run_pt_effects(
161    account: &mut AccountCurrent,
162    exec: &mut Executor,
163    pt: ProgrammableTransaction,
164) -> Result<TransactionEffects, IotaError> {
165    let gas_object = account.new_gas_object(exec);
166    let gas_object_ref = gas_object.compute_object_reference();
167    let kind = TransactionKind::ProgrammableTransaction(pt);
168    let tx_data = TransactionData::new(
169        kind,
170        account.initial_data.account.address,
171        gas_object_ref,
172        GAS,
173        GAS_PRICE,
174    );
175    let signed_txn = to_sender_signed_transaction(tx_data, &account.initial_data.account.key);
176    exec.rt
177        .block_on(send_and_confirm_transaction(&exec.state, None, signed_txn))
178        .map(|(_, effects)| effects.into_data())
179}