iota_replay/
fuzz_mutations.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_types::transaction::TransactionKind;
6use rand::{SeedableRng, seq::SliceRandom};
7
8use crate::fuzz::TransactionKindMutator;
9
10pub mod drop_random_command_suffix;
11pub mod drop_random_commands;
12pub mod shuffle_command_inputs;
13pub mod shuffle_commands;
14pub mod shuffle_transaction_inputs;
15pub mod shuffle_types;
16
17// The number of times that we will try to select a different mutator if the
18// selected one is unable to be applied for some reason.
19const NUM_TRIES: u64 = 5;
20
21// Combiners for `TransactionKindMutator`s:
22// * `RandomMutator` will select a random mutator from a list of mutators
23// * `ChainedMutator` will apply a list of mutators in sequence. If a given
24//   mutator doesn't apply it will be skipped but other mutations both before
25//   and after the failed mutator may still be applied.
26pub struct RandomMutator {
27    pub rng: rand::rngs::StdRng,
28    pub mutators: Vec<Box<dyn TransactionKindMutator + Send + Sync>>,
29    pub num_tries: u64,
30}
31
32pub struct ChainedMutator {
33    pub mutators: Vec<Box<dyn TransactionKindMutator>>,
34}
35
36impl RandomMutator {
37    pub fn new() -> Self {
38        Self {
39            rng: rand::rngs::StdRng::from_seed([0u8; 32]),
40            mutators: vec![],
41            num_tries: NUM_TRIES,
42        }
43    }
44
45    pub fn add_mutator(&mut self, mutator: Box<dyn TransactionKindMutator + Send + Sync>) {
46        self.mutators.push(mutator);
47    }
48
49    pub fn select_mutator(&mut self) -> Option<&mut Box<dyn TransactionKindMutator + Send + Sync>> {
50        self.mutators.choose_mut(&mut self.rng)
51    }
52}
53
54impl Default for RandomMutator {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl TransactionKindMutator for RandomMutator {
61    fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> {
62        for _ in 0..self.num_tries {
63            if let Some(mutator) = self.select_mutator() {
64                return mutator.mutate(transaction_kind);
65            }
66        }
67        None
68    }
69
70    fn reset(&mut self, mutations_per_base: u64) {
71        for mutator in self.mutators.iter_mut() {
72            mutator.reset(mutations_per_base);
73        }
74    }
75}
76
77impl ChainedMutator {
78    pub fn new() -> Self {
79        Self { mutators: vec![] }
80    }
81
82    pub fn add_mutator(&mut self, mutator: Box<dyn TransactionKindMutator>) {
83        self.mutators.push(mutator);
84    }
85}
86
87impl Default for ChainedMutator {
88    fn default() -> Self {
89        Self::new()
90    }
91}
92
93impl TransactionKindMutator for ChainedMutator {
94    fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> {
95        let mut mutated = transaction_kind.clone();
96        let mut num_mutations = 0;
97
98        for mutator in self.mutators.iter_mut() {
99            if let Some(new_mutated) = mutator.mutate(&mutated) {
100                num_mutations += 1;
101                mutated = new_mutated;
102            }
103        }
104
105        if num_mutations == 0 {
106            None
107        } else {
108            Some(mutated)
109        }
110    }
111
112    fn reset(&mut self, mutations_per_base: u64) {
113        for mutator in self.mutators.iter_mut() {
114            mutator.reset(mutations_per_base);
115        }
116    }
117}
118
119pub fn base_fuzzers(num_mutations: u64) -> RandomMutator {
120    let mut mutator = RandomMutator::new();
121    mutator.add_mutator(Box::new(shuffle_commands::ShuffleCommands {
122        rng: rand::rngs::StdRng::from_seed([0u8; 32]),
123        num_mutations_per_base_left: num_mutations,
124    }));
125    mutator.add_mutator(Box::new(shuffle_types::ShuffleTypes {
126        rng: rand::rngs::StdRng::from_seed([0u8; 32]),
127        num_mutations_per_base_left: num_mutations,
128    }));
129    mutator.add_mutator(Box::new(shuffle_command_inputs::ShuffleCommandInputs {
130        rng: rand::rngs::StdRng::from_seed([0u8; 32]),
131        num_mutations_per_base_left: num_mutations,
132    }));
133    mutator.add_mutator(Box::new(
134        shuffle_transaction_inputs::ShuffleTransactionInputs {
135            rng: rand::rngs::StdRng::from_seed([0u8; 32]),
136            num_mutations_per_base_left: num_mutations,
137        },
138    ));
139    mutator.add_mutator(Box::new(drop_random_commands::DropRandomCommands {
140        rng: rand::rngs::StdRng::from_seed([0u8; 32]),
141        num_mutations_per_base_left: num_mutations,
142    }));
143    mutator.add_mutator(Box::new(drop_random_command_suffix::DropCommandSuffix {
144        rng: rand::rngs::StdRng::from_seed([0u8; 32]),
145        num_mutations_per_base_left: num_mutations,
146    }));
147    mutator
148}