phink_lib/fuzzer/
manager.rs1#![allow(unused_imports, unused_variables)]
2use crate::{
3 cli::config::Configuration,
4 contract::{
5 remote::{
6 ContractSetup,
7 FullContractResponse,
8 },
9 selectors::{
10 database::SelectorDatabase,
11 selector::Selector,
12 },
13 },
14 cover::coverage::InputCoverage,
15 fuzzer::parser::{
16 Message,
17 OneInput,
18 Origin,
19 },
20 ResultOf,
21};
22use anyhow::{
23 bail,
24 Context,
25};
26use contract_transcode::ContractMessageTranscoder;
27use sp_runtime::{
28 DispatchError,
29 ModuleError,
30};
31use std::{
32 panic,
33 path::Path,
34 sync::{
35 Arc,
36 Mutex,
37 },
38};
39
40#[derive(Clone)]
41pub struct CampaignManager {
42 setup: ContractSetup,
43 database: SelectorDatabase,
44 configuration: Configuration,
45 transcoder: Arc<Mutex<ContractMessageTranscoder>>,
46}
47
48impl CampaignManager {
49 pub fn new(
50 database: SelectorDatabase,
51 setup: ContractSetup,
52 configuration: Configuration,
53 ) -> ResultOf<Self> {
54 let transcoder = Arc::new(Mutex::new(
55 ContractMessageTranscoder::load(Path::new(&setup.path_to_specs))
56 .context("Cannot instantiante the `ContractMessageTranscoder`")?,
57 ));
58
59 Ok(Self {
60 setup,
61 database,
62 configuration,
63 transcoder,
64 })
65 }
66
67 pub fn config(&self) -> Configuration {
68 self.configuration.clone()
69 }
70
71 pub fn database(&self) -> &SelectorDatabase {
72 &self.database
73 }
74
75 pub fn transcoder(&self) -> Arc<Mutex<ContractMessageTranscoder>> {
76 Arc::clone(&self.transcoder)
77 }
78
79 pub fn check_invariants(
80 &self,
81 responses: &[FullContractResponse],
82 decoded_msgs: &OneInput,
83 catch_trapped_contract: bool,
84 ) {
85 let trapped = responses.iter().filter(|response| response.is_trapped());
86
87 if catch_trapped_contract && trapped.clone().next().is_some() {
90 trapped.for_each(|response| {
91 self.display_trap(decoded_msgs, response);
92 });
93 panic!("\n🫡 Job is done! Please, don't mind the backtrace below/above.\n\n");
95 }
96
97 if let Ok(invariant_tested) = self.are_invariants_failing(decoded_msgs.messages[0].origin) {
99 self.display_invariant(responses.to_vec(), decoded_msgs, invariant_tested);
100 panic!("\n🫡 Job is done! Please, don't mind the backtrace below/above.\n\n"); }
102 }
103
104 pub fn display_trap(&self, message: &OneInput, response: &FullContractResponse) {
105 #[cfg(not(fuzzing))]
106 {
107 println!("\n🤯 A trapped contract got caught! Let's dive into it");
108 println!("\n🐛 IMPORTANT STACKTRACE : {response}\n");
109 println!("🎉 Find below the trace that caused that trapped contract");
110 message.pretty_print(vec![response.clone()]);
111 }
112 }
113
114 #[allow(unused_mut)]
115 pub fn display_invariant(
116 &self,
117 responses: Vec<FullContractResponse>,
118 decoded_msg: &OneInput,
119 mut invariant_tested: Selector,
120 ) {
121 #[cfg(not(fuzzing))]
122 {
123 let hex = self
124 .transcoder()
125 .lock()
126 .unwrap()
127 .decode_contract_message(&mut &*invariant_tested.as_mut())
128 .unwrap();
129
130 println!("\n🤯 An invariant got caught! Let's dive into it");
131 println!("\n🫵 This was caused by `{hex}`\n");
132 println!("🎉 Find below the trace that caused that invariant");
133 decoded_msg.pretty_print(responses);
134 }
135 }
136
137 pub fn are_invariants_failing(&self, origin: Origin) -> ResultOf<Selector> {
139 for invariant in &self.database.to_owned().invariants()? {
140 let invariant_call: FullContractResponse = self.to_owned().setup.call(
141 invariant.as_ref(),
142 origin.into(),
143 0,
144 self.configuration.clone(),
145 );
146 if invariant_call.failed() {
147 return Ok(*invariant);
148 }
149 }
150 bail!("All invariants passed")
151 }
152}