iota_single_node_benchmark/tx_generator/
move_tx_generator.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::HashMap;
6
7use iota_test_transaction_builder::TestTransactionBuilder;
8use iota_types::{
9    base_types::{IotaAddress, ObjectID, ObjectRef, SequenceNumber},
10    programmable_transaction_builder::ProgrammableTransactionBuilder,
11    transaction::{CallArg, DEFAULT_VALIDATOR_GAS_PRICE, ObjectArg, Transaction},
12};
13use move_core_types::identifier::Identifier;
14
15use crate::{mock_account::Account, tx_generator::TxGenerator};
16
17pub struct MoveTxGenerator {
18    move_package: ObjectID,
19    num_transfers: u64,
20    use_native_transfer: bool,
21    computation: u8,
22    root_objects: HashMap<IotaAddress, ObjectRef>,
23    shared_objects: Vec<(ObjectID, SequenceNumber)>,
24    num_mints: u16,
25    nft_size: u16,
26    use_batch_mint: bool,
27}
28
29impl MoveTxGenerator {
30    pub fn new(
31        move_package: ObjectID,
32        num_transfers: u64,
33        use_native_transfer: bool,
34        computation: u8,
35        root_objects: HashMap<IotaAddress, ObjectRef>,
36        shared_objects: Vec<(ObjectID, SequenceNumber)>,
37        num_mints: u16,
38        nft_size: u16,
39        use_batch_mint: bool,
40    ) -> Self {
41        Self {
42            move_package,
43            num_transfers,
44            use_native_transfer,
45            computation,
46            root_objects,
47            shared_objects,
48            num_mints,
49            nft_size,
50            use_batch_mint,
51        }
52    }
53}
54
55impl TxGenerator for MoveTxGenerator {
56    fn generate_tx(&self, account: Account) -> Transaction {
57        let pt = {
58            let mut builder = ProgrammableTransactionBuilder::new();
59            // Step 1: transfer `num_transfers` objects.
60            // First object in the gas_objects is the gas object and we are not transferring
61            // it.
62            for i in 1..=self.num_transfers {
63                let object = account.gas_objects[i as usize];
64                if self.use_native_transfer {
65                    builder.transfer_object(account.sender, object).unwrap();
66                } else {
67                    builder
68                        .move_call(
69                            self.move_package,
70                            Identifier::new("benchmark").unwrap(),
71                            Identifier::new("transfer_coin").unwrap(),
72                            vec![],
73                            vec![CallArg::Object(ObjectArg::ImmOrOwnedObject(object))],
74                        )
75                        .unwrap();
76                }
77            }
78            for shared_object in &self.shared_objects {
79                builder
80                    .move_call(
81                        self.move_package,
82                        Identifier::new("benchmark").unwrap(),
83                        Identifier::new("increment_shared_counter").unwrap(),
84                        vec![],
85                        vec![CallArg::Object(ObjectArg::SharedObject {
86                            id: shared_object.0,
87                            initial_shared_version: shared_object.1,
88                            mutable: true,
89                        })],
90                    )
91                    .unwrap();
92            }
93
94            if !self.root_objects.is_empty() {
95                // Step 2: Read all dynamic fields from the root object.
96                let root_object = self.root_objects.get(&account.sender).unwrap();
97                let root_object_arg = builder
98                    .obj(ObjectArg::ImmOrOwnedObject(*root_object))
99                    .unwrap();
100                builder.programmable_move_call(
101                    self.move_package,
102                    Identifier::new("benchmark").unwrap(),
103                    Identifier::new("read_dynamic_fields").unwrap(),
104                    vec![],
105                    vec![root_object_arg],
106                );
107            }
108
109            if self.computation > 0 {
110                // Step 3: Run some computation.
111                let computation_arg = builder.pure(self.computation as u64 * 100).unwrap();
112                builder.programmable_move_call(
113                    self.move_package,
114                    Identifier::new("benchmark").unwrap(),
115                    Identifier::new("run_computation").unwrap(),
116                    vec![],
117                    vec![computation_arg],
118                );
119            }
120            if self.num_mints > 0 {
121                // Step 4: Mint some NFTs
122                let mut contents = Vec::new();
123                assert!(self.nft_size >= 32, "NFT size must be at least 32 bytes");
124                for _ in 0..self.nft_size - 32 {
125                    contents.push(7u8)
126                }
127                if self.use_batch_mint {
128                    // create a vector of sender addresses to pass to batch_mint
129                    let mut recipients = Vec::new();
130                    for _ in 0..self.num_mints {
131                        recipients.push(account.sender)
132                    }
133                    let args = vec![
134                        builder.pure(recipients).unwrap(),
135                        builder.pure(contents).unwrap(),
136                    ];
137                    builder.programmable_move_call(
138                        self.move_package,
139                        Identifier::new("benchmark").unwrap(),
140                        Identifier::new("batch_mint").unwrap(),
141                        vec![],
142                        args,
143                    );
144                } else {
145                    // create PTB with a command that transfers each
146                    for _ in 0..self.num_mints {
147                        let args = vec![
148                            builder.pure(account.sender).unwrap(),
149                            builder.pure(contents.clone()).unwrap(),
150                        ];
151                        builder.programmable_move_call(
152                            self.move_package,
153                            Identifier::new("benchmark").unwrap(),
154                            Identifier::new("mint_one").unwrap(),
155                            vec![],
156                            args,
157                        );
158                    }
159                }
160            }
161            builder.finish()
162        };
163        TestTransactionBuilder::new(
164            account.sender,
165            account.gas_objects[0],
166            DEFAULT_VALIDATOR_GAS_PRICE,
167        )
168        .programmable(pt)
169        .build_and_sign(account.keypair.as_ref())
170    }
171
172    fn name(&self) -> &'static str {
173        "Programmable Move Transaction Generator"
174    }
175}