1use crate::{
2 cli::config::OriginFuzzingOption,
3 contract::{
4 remote::{
5 BalanceOf,
6 ContractResponse,
7 FullContractResponse,
8 },
9 runtime::Runtime,
10 selectors::selector::Selector,
11 },
12 fuzzer::manager::CampaignManager,
13 ResultOf,
14};
15use contract_transcode::{
16 ContractMessageTranscoder,
17 Value,
18};
19use prettytable::{
20 Cell,
21 Row,
22 Table,
23};
24use serde_derive::Serialize;
25use sp_core::crypto::AccountId32;
26use std::fmt::{
27 Display,
28 Formatter,
29};
30use OriginFuzzingOption::{
31 DisableOriginFuzzing,
32 EnableOriginFuzzing,
33};
34
35pub const DELIMITER: [u8; 8] = [42; 8]; pub const MIN_SEED_LEN: usize = 9;
37#[derive(Clone, Copy)]
41pub struct Data<'a> {
42 pub data: &'a [u8],
43 pub pointer: usize,
44 pub size: usize,
45 pub max_messages_per_exec: usize,
46}
47
48#[derive(Debug, Clone, Serialize)]
49pub struct Message {
50 pub is_payable: bool,
51 pub payload: Vec<u8>,
52 pub value_token: BalanceOf<Runtime>,
53 pub message_metadata: Value,
54 pub origin: Origin,
55}
56
57impl Message {
58 pub fn display_with_reply(&self, reply: &ContractResponse) -> String {
59 format!(
60 "ā½ļø Gas required: {}\n\
61 š„ Gas consumed: {}\n\
62 š§ Origin: {:?} ({})\n\
63 š¾ Storage deposit: {:?}{}",
64 reply.gas_required,
65 reply.gas_consumed,
66 self.origin,
67 AccountId32::new([self.origin.into(); 32]),
68 reply.storage_deposit,
69 if self.is_payable {
70 format!(
71 "\nšø Message was payable and {} units were transferred",
72 self.value_token
73 )
74 } else {
75 String::new()
76 }
77 )
78 }
79 pub fn print(&self) -> String {
80 format!(
81 "Payload:\t0x{}\n\
82 Origin:\t{:?} (identifier: {})\n\
83 {}\
84 Message:\t{}\n\n",
85 hex::encode(&self.payload),
86 AccountId32::new([self.origin.into(); 32]),
87 self.origin.0,
88 if self.is_payable {
89 format!("Transfered: {}\n", self.value_token)
90 } else {
91 String::new()
92 },
93 self.message_metadata
94 )
95 }
96}
97
98impl Display for Message {
99 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
100 f.write_str(self.message_metadata.to_string().as_str())
101 }
102}
103
104#[derive(Debug, Clone, Serialize)]
105pub struct OneInput {
106 pub messages: Vec<Message>,
107 pub fuzz_option: OriginFuzzingOption,
108 pub raw_binary: Vec<u8>,
109}
110
111impl OneInput {
112 #[allow(dead_code)]
114 pub fn pretty_print(&self, responses: Vec<FullContractResponse>) {
115 println!("\nš± Executing new seed");
116 let mut table = Table::new();
117 table.add_row(Row::new(vec![Cell::new("Message"), Cell::new("Details")]));
118
119 for (response, message) in responses.iter().zip(&self.messages) {
120 let call_description = message.message_metadata.to_string();
121 let debug = message.display_with_reply(response.get());
122
123 table.add_row(Row::new(vec![
124 Cell::new(&call_description),
125 Cell::new(&debug),
126 ]));
127 }
128
129 table.printstd();
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
134pub struct Origin(pub u8);
135impl Default for Origin {
136 fn default() -> Self {
137 Origin(1)
138 }
139}
140impl From<u8> for Origin {
141 fn from(value: u8) -> Self {
142 Origin(value)
143 }
144}
145impl From<Origin> for u8 {
146 fn from(origin: Origin) -> Self {
147 origin.0
148 }
149}
150
151impl Data<'_> {
152 fn size_limit_reached(&self) -> bool {
153 self.size >= self.max_messages_per_exec
154 }
155}
156
157impl<'a> Iterator for Data<'a> {
158 type Item = &'a [u8];
159
160 fn next(&mut self) -> Option<Self::Item> {
161 if self.size_limit_reached() {
162 return None;
163 }
164 if self.max_messages_per_exec == 1 {
166 let res = &self.data[self.pointer..];
167 self.pointer = self.data.len();
168 self.size += 1;
169 return if res.len() >= MIN_SEED_LEN {
170 Some(res)
171 } else {
172 None
173 };
174 }
175
176 loop {
177 if self.data.len() <= self.pointer {
178 return None;
179 }
180 let next_delimiter = self.data[self.pointer..]
181 .windows(DELIMITER.len())
182 .position(|window| window == DELIMITER);
183
184 let next_pointer = match next_delimiter {
185 Some(delimiter) => self.pointer + delimiter,
186 None => self.data.len(),
187 };
188
189 let res = &self.data[self.pointer..next_pointer];
190 self.pointer = next_pointer + DELIMITER.len();
191
192 if res.len() >= MIN_SEED_LEN {
193 self.size += 1;
194 return Some(res);
195 }
196 }
197 }
198}
199
200pub fn try_parse_input(bytes: &[u8], manager: CampaignManager) -> Option<OneInput> {
201 let config = manager.config();
202 let data = Data {
203 data: bytes,
204 pointer: 0,
205 size: 0,
206 max_messages_per_exec: config.max_messages_per_exec.unwrap_or_default(),
207 };
208
209 let mut input = OneInput {
210 messages: vec![],
211 fuzz_option: config.should_fuzz_origin(),
212 raw_binary: Vec::new(),
213 };
214
215 let arc = manager.transcoder();
216 let guard = arc.try_lock().expect("Failed on `try_lock`");
217
218 for payload in data {
219 let origin = match input.fuzz_option {
220 EnableOriginFuzzing => Origin(payload[4]),
221 DisableOriginFuzzing => Origin::default(),
222 };
223 let mut encoded_message = vec![0u8; payload.len() - 5];
224 encoded_message.copy_from_slice(&payload[5..]);
225
226 let selector: [u8; 4] = encoded_message[0..4].try_into().expect("[0..4] to u8 fail");
227 let slctr = Selector::from(selector);
228 let db = manager.database();
229
230 if db.contains_invariant(&slctr) || !db.contains_message(&slctr) {
232 return None;
233 }
234
235 let mut encoded_cloned = encoded_message.clone();
236
237 match decode_contract_message(&guard, &mut encoded_cloned) {
238 Ok(message_metadata) => {
239 if data.max_messages_per_exec != 0
240 && input.messages.len() <= data.max_messages_per_exec
241 {
242 let is_payable: bool = db.is_payable(&slctr);
243 let mut value_token: u128 = 0;
244 if is_payable {
245 value_token = u32::from_ne_bytes(payload[0..4].try_into().unwrap()) as u128 }
247 input.raw_binary = Vec::from(bytes);
248 input.messages.push(Message {
249 is_payable,
250 payload: encoded_message,
251 value_token,
252 message_metadata,
253 origin,
254 });
255 }
256 }
257 Err(_) => {
258 return None;
259 }
260 }
261 }
262
263 if !input.messages.is_empty() {
264 return Some(input);
265 }
266 None
267}
268
269pub fn decode_contract_message(
270 guard: &ContractMessageTranscoder,
271 data: &mut Vec<u8>,
272) -> ResultOf<Value> {
273 use contract_transcode::Map;
274 use std::io::Read;
275
276 let mut data_as_slice = data.as_slice();
277 let mut msg_selector: [u8; 4] = [0u8; 4];
278 data_as_slice.read_exact(&mut msg_selector)?;
279 let msg_spec = guard
280 .metadata()
281 .spec()
282 .messages()
283 .iter()
284 .find(|x| msg_selector == x.selector().to_bytes())
285 .ok_or_else(|| {
286 anyhow::anyhow!(
287 "Message with selector {} not found in contract metadata",
288 hex::encode_upper(msg_selector)
289 )
290 })?;
291
292 let mut args = Vec::new();
293 for arg in msg_spec.args() {
294 let name = arg.label().to_string();
295 let value = guard.decode(arg.ty().ty().id, &mut data_as_slice)?;
296 args.push((Value::String(name), value));
297 }
298
299 if !data_as_slice.is_empty() {
300 return Err(anyhow::anyhow!(
301 "input length was longer than expected by {} byte(s).\n `{}` bytes were left unread",
302 data_as_slice.len(),
303 hex::encode_upper(data)
304 ));
305 }
306 let name = msg_spec.label().to_string();
307 let map = Map::new(Some(&name), args.into_iter().collect());
308
309 Ok(Value::Map(map))
310}
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_data_iterator() {
317 let input = [
318 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 42, 42, 42, 42, 42, 42, 42, 42, 5, 6, 7, 23, 123, 1, 8,
319 12, 13, 14,
320 ];
321 let data = Data {
322 data: &input,
323 pointer: 0,
324 size: 0,
325 max_messages_per_exec: 2,
326 };
327
328 let result: Vec<&[u8]> = data.collect();
329 assert_eq!(
330 result,
331 vec![
332 &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
333 &[5, 6, 7, 23, 123, 1, 8, 12, 13, 14]
334 ]
335 );
336 }
337
338 #[test]
339 fn test_data_size_limit() {
340 let input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
341 let mut data = Data {
342 data: &input,
343 pointer: 0,
344 size: 0,
345 max_messages_per_exec: 1,
346 };
347
348 assert_eq!(data.next(), Some(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10][..]));
349 assert_eq!(data.next(), None);
350 }
351
352 #[test]
353 fn test_origin_default() {
354 assert_eq!(Origin::default(), Origin(1));
355 }
356
357 #[test]
358 fn test_origin_from_u8() {
359 assert_eq!(Origin::from(5), Origin(5));
360 }
361
362 #[test]
363 fn test_u8_from_origin() {
364 assert_eq!(u8::from(Origin(3)), 3);
365 }
366}