forked from MystenLabs/sui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[replay-fuzz] Add additional mutations to the transaction-replay fuzz…
…er (MystenLabs#12079) ## Description Adds a bunch of new mutations to the transaction replay fuzzer, and also adds a way to random select a different mutators from a set of them. ## Test Plan Ran them locally
- Loading branch information
Showing
9 changed files
with
398 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use rand::{seq::SliceRandom, SeedableRng}; | ||
use sui_types::transaction::TransactionKind; | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
|
||
pub mod drop_random_command_suffix; | ||
pub mod drop_random_commands; | ||
pub mod shuffle_command_inputs; | ||
pub mod shuffle_commands; | ||
pub mod shuffle_transaction_inputs; | ||
pub mod shuffle_types; | ||
|
||
// The number of times that we will try to select a different mutator if the selected one is unable | ||
// to be applied for some reason. | ||
const NUM_TRIES: u64 = 5; | ||
|
||
// Combiners for `TransactionKindMutator`s: | ||
// * `RandomMutator` will select a random mutator from a list of mutators | ||
// * `ChainedMutator` will apply a list of mutators in sequence. If a given mutator doesn't apply | ||
// it will be skipped but other mutations both before and after the failed mutator may still be applied. | ||
pub struct RandomMutator { | ||
pub rng: rand::rngs::StdRng, | ||
pub mutators: Vec<Box<dyn TransactionKindMutator + Send + Sync>>, | ||
pub num_tries: u64, | ||
} | ||
|
||
pub struct ChainedMutator { | ||
pub mutators: Vec<Box<dyn TransactionKindMutator>>, | ||
} | ||
|
||
impl RandomMutator { | ||
pub fn new() -> Self { | ||
Self { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
mutators: vec![], | ||
num_tries: NUM_TRIES, | ||
} | ||
} | ||
|
||
pub fn add_mutator(&mut self, mutator: Box<dyn TransactionKindMutator + Send + Sync>) { | ||
self.mutators.push(mutator); | ||
} | ||
|
||
pub fn select_mutator(&mut self) -> Option<&mut Box<dyn TransactionKindMutator + Send + Sync>> { | ||
self.mutators.choose_mut(&mut self.rng) | ||
} | ||
} | ||
|
||
impl Default for RandomMutator { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
impl TransactionKindMutator for RandomMutator { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
for _ in 0..self.num_tries { | ||
if let Some(mutator) = self.select_mutator() { | ||
return mutator.mutate(transaction_kind); | ||
} | ||
} | ||
None | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
for mutator in self.mutators.iter_mut() { | ||
mutator.reset(mutations_per_base); | ||
} | ||
} | ||
} | ||
|
||
impl ChainedMutator { | ||
pub fn new() -> Self { | ||
Self { mutators: vec![] } | ||
} | ||
|
||
pub fn add_mutator(&mut self, mutator: Box<dyn TransactionKindMutator>) { | ||
self.mutators.push(mutator); | ||
} | ||
} | ||
|
||
impl Default for ChainedMutator { | ||
fn default() -> Self { | ||
Self::new() | ||
} | ||
} | ||
|
||
impl TransactionKindMutator for ChainedMutator { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
let mut mutated = transaction_kind.clone(); | ||
let mut num_mutations = 0; | ||
|
||
for mutator in self.mutators.iter_mut() { | ||
if let Some(new_mutated) = mutator.mutate(&mutated) { | ||
num_mutations += 1; | ||
mutated = new_mutated; | ||
} | ||
} | ||
|
||
if num_mutations == 0 { | ||
None | ||
} else { | ||
Some(mutated) | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
for mutator in self.mutators.iter_mut() { | ||
mutator.reset(mutations_per_base); | ||
} | ||
} | ||
} | ||
|
||
pub fn base_fuzzers(num_mutations: u64) -> RandomMutator { | ||
let mut mutator = RandomMutator::new(); | ||
mutator.add_mutator(Box::new(shuffle_commands::ShuffleCommands { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
})); | ||
mutator.add_mutator(Box::new(shuffle_types::ShuffleTypes { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
})); | ||
mutator.add_mutator(Box::new(shuffle_command_inputs::ShuffleCommandInputs { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
})); | ||
mutator.add_mutator(Box::new( | ||
shuffle_transaction_inputs::ShuffleTransactionInputs { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
}, | ||
)); | ||
mutator.add_mutator(Box::new(drop_random_commands::DropRandomCommands { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
})); | ||
mutator.add_mutator(Box::new(drop_random_command_suffix::DropCommandSuffix { | ||
rng: rand::rngs::StdRng::from_seed([0u8; 32]), | ||
num_mutations_per_base_left: num_mutations, | ||
})); | ||
mutator | ||
} |
39 changes: 39 additions & 0 deletions
39
crates/sui-replay/src/fuzz_mutations/drop_random_command_suffix.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
use rand::Rng; | ||
use sui_types::transaction::TransactionKind; | ||
use tracing::info; | ||
|
||
pub struct DropCommandSuffix { | ||
pub rng: rand::rngs::StdRng, | ||
pub num_mutations_per_base_left: u64, | ||
} | ||
|
||
impl TransactionKindMutator for DropCommandSuffix { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
if self.num_mutations_per_base_left == 0 { | ||
// Nothing else to do | ||
return None; | ||
} | ||
|
||
self.num_mutations_per_base_left -= 1; | ||
if let TransactionKind::ProgrammableTransaction(mut p) = transaction_kind.clone() { | ||
if p.commands.is_empty() { | ||
return None; | ||
} | ||
let slice_index = self.rng.gen_range(0..p.commands.len()); | ||
p.commands.truncate(slice_index); | ||
info!("Mutation: Dropping command suffix"); | ||
Some(TransactionKind::ProgrammableTransaction(p)) | ||
} else { | ||
// Other types not supported yet | ||
None | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
self.num_mutations_per_base_left = mutations_per_base; | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
crates/sui-replay/src/fuzz_mutations/drop_random_commands.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
use rand::seq::SliceRandom; | ||
use sui_types::transaction::TransactionKind; | ||
use tracing::info; | ||
|
||
pub struct DropRandomCommands { | ||
pub rng: rand::rngs::StdRng, | ||
pub num_mutations_per_base_left: u64, | ||
} | ||
|
||
impl TransactionKindMutator for DropRandomCommands { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
if self.num_mutations_per_base_left == 0 { | ||
// Nothing else to do | ||
return None; | ||
} | ||
|
||
self.num_mutations_per_base_left -= 1; | ||
if let TransactionKind::ProgrammableTransaction(mut p) = transaction_kind.clone() { | ||
if p.commands.is_empty() { | ||
return None; | ||
} | ||
p.commands = p | ||
.commands | ||
.choose_multiple(&mut self.rng, p.commands.len() - 1) | ||
.cloned() | ||
.collect(); | ||
info!("Mutation: Dropping random commands"); | ||
Some(TransactionKind::ProgrammableTransaction(p)) | ||
} else { | ||
// Other types not supported yet | ||
None | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
self.num_mutations_per_base_left = mutations_per_base; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
crates/sui-replay/src/fuzz_mutations/shuffle_command_inputs.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
use rand::seq::SliceRandom; | ||
use sui_types::transaction::{Command, TransactionKind}; | ||
use tracing::info; | ||
|
||
pub struct ShuffleCommandInputs { | ||
pub rng: rand::rngs::StdRng, | ||
pub num_mutations_per_base_left: u64, | ||
} | ||
|
||
impl ShuffleCommandInputs { | ||
fn shuffle_command(&mut self, command: &mut Command) { | ||
match command { | ||
Command::MakeMoveVec(_, ref mut args) | ||
| Command::MergeCoins(_, ref mut args) | ||
| Command::SplitCoins(_, ref mut args) | ||
| Command::TransferObjects(ref mut args, _) => { | ||
args.shuffle(&mut self.rng); | ||
} | ||
Command::MoveCall(ref mut pt) => pt.arguments.shuffle(&mut self.rng), | ||
Command::Publish(_, _) => (), | ||
Command::Upgrade(_, _, _, _) => (), | ||
} | ||
} | ||
} | ||
|
||
impl TransactionKindMutator for ShuffleCommandInputs { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
if self.num_mutations_per_base_left == 0 { | ||
// Nothing else to do | ||
return None; | ||
} | ||
|
||
self.num_mutations_per_base_left -= 1; | ||
if let TransactionKind::ProgrammableTransaction(mut p) = transaction_kind.clone() { | ||
for command in &mut p.commands { | ||
self.shuffle_command(command); | ||
} | ||
info!("Mutation: Shuffling command inputs"); | ||
Some(TransactionKind::ProgrammableTransaction(p)) | ||
} else { | ||
// Other types not supported yet | ||
None | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
self.num_mutations_per_base_left = mutations_per_base; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
use rand::seq::SliceRandom; | ||
use sui_types::transaction::TransactionKind; | ||
use tracing::info; | ||
|
||
pub struct ShuffleCommands { | ||
pub rng: rand::rngs::StdRng, | ||
pub num_mutations_per_base_left: u64, | ||
} | ||
|
||
impl TransactionKindMutator for ShuffleCommands { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
if self.num_mutations_per_base_left == 0 { | ||
// Nothing else to do | ||
return None; | ||
} | ||
|
||
self.num_mutations_per_base_left -= 1; | ||
if let TransactionKind::ProgrammableTransaction(mut p) = transaction_kind.clone() { | ||
p.commands.shuffle(&mut self.rng); | ||
info!("Mutation: Shuffling commands"); | ||
Some(TransactionKind::ProgrammableTransaction(p)) | ||
} else { | ||
// Other types not supported yet | ||
None | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
self.num_mutations_per_base_left = mutations_per_base; | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
crates/sui-replay/src/fuzz_mutations/shuffle_transaction_inputs.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::fuzz::TransactionKindMutator; | ||
use rand::seq::SliceRandom; | ||
use sui_types::transaction::TransactionKind; | ||
use tracing::info; | ||
|
||
pub struct ShuffleTransactionInputs { | ||
pub rng: rand::rngs::StdRng, | ||
pub num_mutations_per_base_left: u64, | ||
} | ||
|
||
impl TransactionKindMutator for ShuffleTransactionInputs { | ||
fn mutate(&mut self, transaction_kind: &TransactionKind) -> Option<TransactionKind> { | ||
if self.num_mutations_per_base_left == 0 { | ||
// Nothing else to do | ||
return None; | ||
} | ||
|
||
self.num_mutations_per_base_left -= 1; | ||
if let TransactionKind::ProgrammableTransaction(mut p) = transaction_kind.clone() { | ||
p.inputs.shuffle(&mut self.rng); | ||
info!("Mutation: Shuffling transaction inputs"); | ||
Some(TransactionKind::ProgrammableTransaction(p)) | ||
} else { | ||
// Other types not supported yet | ||
None | ||
} | ||
} | ||
|
||
fn reset(&mut self, mutations_per_base: u64) { | ||
self.num_mutations_per_base_left = mutations_per_base; | ||
} | ||
} |
Oops, something went wrong.