sui_replay/
fuzz_mutations.rs

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