1use std::collections::BTreeMap;
17use std::fmt;
18use std::sync::Arc;
19
20use serde::de::{SeqAccess, Visitor};
21use serde::ser::SerializeStruct;
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23use thiserror::Error;
24
25#[derive(Debug, Error)]
26pub enum SoldbError {
27 #[error("{0}")]
29 Message(String),
30 #[error("")]
36 AlreadyReported,
37}
38
39pub type SoldbResult<T> = Result<T, SoldbError>;
40
41#[derive(Debug, Clone, Eq)]
54pub struct TraceStep {
55 pub pc: u64,
56 pub op: Word,
59 pub gas: u64,
60 pub gas_cost: u64,
61 pub depth: u64,
62 pub stack: Vec<Word>,
64 pub memory: Option<String>,
66 pub storage: Option<BTreeMap<String, String>>,
68 pub error: Option<String>,
69 pub snapshot: StepSnapshot,
70}
71
72impl TraceStep {
73 #[must_use]
75 pub fn new(
76 pc: u64,
77 op: Word,
78 gas: u64,
79 gas_cost: u64,
80 depth: u64,
81 error: Option<String>,
82 snapshot: StepSnapshot,
83 ) -> Self {
84 Self {
85 pc,
86 op,
87 gas,
88 gas_cost,
89 depth,
90 stack: Vec::new(),
91 memory: None,
92 storage: None,
93 error,
94 snapshot,
95 }
96 }
97
98 #[must_use]
108 pub fn snapshot_ref(&self) -> StepSnapshotRef<'_> {
109 static EMPTY_STORAGE: BTreeMap<String, String> = BTreeMap::new();
110 static EMPTY_STORAGE_DIFF: BTreeMap<String, StorageChange> = BTreeMap::new();
111
112 if !self.snapshot.is_empty() {
113 return StepSnapshotRef {
114 stack: &self.snapshot.stack,
115 memory: self.snapshot.memory.as_deref(),
116 storage: &self.snapshot.storage,
117 storage_diff: &self.snapshot.storage_diff,
118 transient_storage: &self.snapshot.transient_storage,
119 return_data: self.snapshot.return_data.as_deref(),
120 };
121 }
122 StepSnapshotRef {
123 stack: &self.stack,
124 memory: self.memory.as_deref(),
125 storage: self.storage.as_ref().unwrap_or(&EMPTY_STORAGE),
126 storage_diff: &EMPTY_STORAGE_DIFF,
127 transient_storage: &EMPTY_STORAGE,
128 return_data: None,
129 }
130 }
131
132 #[must_use]
137 pub fn normalized_snapshot(&self) -> StepSnapshot {
138 self.snapshot_ref().to_owned_snapshot()
139 }
140
141 fn normalized(mut self) -> Self {
144 if self.snapshot.is_empty() {
145 self.snapshot = StepSnapshot {
146 stack: std::mem::take(&mut self.stack),
147 memory: self.memory.take().map(Arc::from),
148 storage: Arc::new(self.storage.take().unwrap_or_default()),
149 storage_diff: BTreeMap::new(),
150 transient_storage: BTreeMap::new(),
151 return_data: None,
152 };
153 } else {
154 self.stack = Vec::new();
155 self.memory = None;
156 self.storage = None;
157 }
158 self
159 }
160}
161
162impl PartialEq for TraceStep {
163 fn eq(&self, other: &Self) -> bool {
165 self.pc == other.pc
166 && self.op == other.op
167 && self.gas == other.gas
168 && self.gas_cost == other.gas_cost
169 && self.depth == other.depth
170 && self.error == other.error
171 && self.snapshot_ref() == other.snapshot_ref()
172 }
173}
174
175impl Serialize for TraceStep {
176 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
179 let snapshot = self.snapshot_ref();
180 let mut state = serializer.serialize_struct("TraceStep", 10)?;
181 state.serialize_field("pc", &self.pc)?;
182 state.serialize_field("op", &self.op)?;
183 state.serialize_field("gas", &self.gas)?;
184 state.serialize_field("gas_cost", &self.gas_cost)?;
185 state.serialize_field("depth", &self.depth)?;
186 state.serialize_field("stack", snapshot.stack)?;
187 state.serialize_field("memory", &snapshot.memory)?;
188 state.serialize_field("storage", snapshot.storage)?;
189 state.serialize_field("error", &self.error)?;
190 state.serialize_field("snapshot", &snapshot)?;
191 state.end()
192 }
193}
194
195#[derive(Deserialize)]
197struct TraceStepRepr {
198 pc: u64,
199 op: String,
200 gas: u64,
201 gas_cost: u64,
202 depth: u64,
203 #[serde(default)]
204 stack: Vec<String>,
205 #[serde(default)]
206 memory: Option<String>,
207 #[serde(default)]
208 storage: Option<BTreeMap<String, String>>,
209 #[serde(default)]
210 error: Option<String>,
211 #[serde(default)]
212 snapshot: StepSnapshot,
213}
214
215impl<'de> Deserialize<'de> for TraceStep {
216 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
217 let repr = TraceStepRepr::deserialize(deserializer)?;
218 Ok(TraceStep {
219 pc: repr.pc,
220 op: Arc::from(repr.op),
221 gas: repr.gas,
222 gas_cost: repr.gas_cost,
223 depth: repr.depth,
224 stack: repr.stack.into_iter().map(Arc::from).collect(),
225 memory: repr.memory,
226 storage: repr.storage,
227 error: repr.error,
228 snapshot: repr.snapshot,
229 }
230 .normalized())
231 }
232}
233
234pub type Word = Arc<str>;
243
244#[derive(Debug, Default)]
249pub struct WordInterner {
250 words: std::collections::HashMap<Word, Word>,
251}
252
253impl WordInterner {
254 #[must_use]
255 pub fn new() -> Self {
256 Self::default()
257 }
258
259 #[must_use]
261 pub fn intern(&mut self, word: &str) -> Word {
262 if let Some(shared) = self.words.get(word) {
263 return Arc::clone(shared);
264 }
265 let shared: Word = Arc::from(word);
266 self.words.insert(Arc::clone(&shared), Arc::clone(&shared));
267 shared
268 }
269
270 pub fn intern_stack(&mut self, stack: &mut [Word]) {
272 for word in stack {
273 *word = self.intern(word);
274 }
275 }
276
277 #[must_use]
279 pub fn len(&self) -> usize {
280 self.words.len()
281 }
282
283 #[must_use]
284 pub fn is_empty(&self) -> bool {
285 self.words.is_empty()
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
294pub struct StepSnapshotRef<'a> {
295 pub stack: &'a [Word],
296 pub memory: Option<&'a str>,
297 pub storage: &'a BTreeMap<String, String>,
298 pub storage_diff: &'a BTreeMap<String, StorageChange>,
299 #[serde(skip_serializing_if = "is_empty_ref")]
300 pub transient_storage: &'a BTreeMap<String, String>,
301 #[serde(skip_serializing_if = "Option::is_none")]
302 pub return_data: Option<&'a str>,
303}
304
305fn is_empty_ref(map: &&BTreeMap<String, String>) -> bool {
306 map.is_empty()
307}
308
309impl StepSnapshotRef<'_> {
310 #[must_use]
312 pub fn to_owned_snapshot(&self) -> StepSnapshot {
313 StepSnapshot {
314 stack: self.stack.to_vec(),
315 memory: self.memory.map(Arc::from),
316 storage: Arc::new(self.storage.clone()),
317 storage_diff: self.storage_diff.clone(),
318 transient_storage: self.transient_storage.clone(),
319 return_data: self.return_data.map(Arc::from),
320 }
321 }
322}
323
324#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
331#[serde(from = "StepSnapshotRepr")]
332pub struct StepSnapshot {
333 pub stack: Vec<Word>,
334 pub memory: Option<Arc<str>>,
336 pub storage: Arc<BTreeMap<String, String>>,
337 pub storage_diff: BTreeMap<String, StorageChange>,
338 pub transient_storage: BTreeMap<String, String>,
340 pub return_data: Option<Arc<str>>,
342}
343
344impl StepSnapshot {
345 #[must_use]
347 pub fn new(
348 stack: Vec<Word>,
349 memory: Option<String>,
350 storage: BTreeMap<String, String>,
351 storage_diff: BTreeMap<String, StorageChange>,
352 ) -> Self {
353 Self {
354 stack,
355 memory: memory.map(Arc::from),
356 storage: Arc::new(storage),
357 storage_diff,
358 transient_storage: BTreeMap::new(),
359 return_data: None,
360 }
361 }
362
363 #[must_use]
365 pub fn with_transient_storage(mut self, transient_storage: BTreeMap<String, String>) -> Self {
366 self.transient_storage = transient_storage;
367 self
368 }
369
370 #[must_use]
372 pub fn with_return_data(mut self, return_data: Option<String>) -> Self {
373 self.return_data = return_data.filter(|data| !data.is_empty()).map(Arc::from);
374 self
375 }
376
377 #[must_use]
378 pub fn is_empty(&self) -> bool {
379 self.stack.is_empty()
380 && self.memory.is_none()
381 && self.storage.is_empty()
382 && self.storage_diff.is_empty()
383 && self.transient_storage.is_empty()
384 && self.return_data.is_none()
385 }
386}
387
388impl Serialize for StepSnapshot {
389 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
390 let mut state = serializer.serialize_struct("StepSnapshot", 6)?;
391 state.serialize_field("stack", &self.stack)?;
392 state.serialize_field("memory", &self.memory.as_deref())?;
393 state.serialize_field("storage", &*self.storage)?;
394 state.serialize_field("storage_diff", &self.storage_diff)?;
395 if self.transient_storage.is_empty() {
397 state.skip_field("transient_storage")?;
398 } else {
399 state.serialize_field("transient_storage", &self.transient_storage)?;
400 }
401 if self.return_data.is_none() {
402 state.skip_field("return_data")?;
403 } else {
404 state.serialize_field("return_data", &self.return_data.as_deref())?;
405 }
406 state.end()
407 }
408}
409
410#[derive(Deserialize, Default)]
411struct StepSnapshotRepr {
412 #[serde(default)]
413 stack: Vec<Word>,
414 #[serde(default)]
415 memory: Option<String>,
416 #[serde(default)]
417 storage: BTreeMap<String, String>,
418 #[serde(default)]
419 storage_diff: BTreeMap<String, StorageChange>,
420 #[serde(default)]
421 transient_storage: BTreeMap<String, String>,
422 #[serde(default)]
423 return_data: Option<String>,
424}
425
426impl From<StepSnapshotRepr> for StepSnapshot {
427 fn from(repr: StepSnapshotRepr) -> Self {
428 Self::new(repr.stack, repr.memory, repr.storage, repr.storage_diff)
429 .with_transient_storage(repr.transient_storage)
430 .with_return_data(repr.return_data)
431 }
432}
433
434#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
435pub struct StorageChange {
436 pub before: Option<String>,
437 pub after: Option<String>,
438}
439
440#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
447#[serde(from = "TransactionTraceRepr")]
448pub struct TransactionTrace {
449 pub tx_hash: Option<String>,
450 pub from_addr: String,
451 pub to_addr: Option<String>,
452 pub value: String,
453 pub input_data: String,
454 pub gas_used: u64,
455 pub output: String,
456 pub success: bool,
457 pub error: Option<String>,
458 pub debug_trace_available: bool,
459 pub contract_address: Option<String>,
460 #[serde(default)]
461 pub backend: Option<String>,
462 #[serde(default)]
463 pub capabilities: TraceCapabilities,
464 #[serde(default)]
465 pub artifacts: TraceArtifacts,
466 pub steps: Vec<TraceStep>,
467}
468
469impl TransactionTrace {
470 pub fn share_unchanged_state(&mut self) {
475 for index in 1..self.steps.len() {
476 let (before, after) = self.steps.split_at_mut(index);
477 share_snapshot(&before[index - 1], &mut after[0]);
478 }
479 }
480}
481
482#[derive(Deserialize)]
484struct TransactionTraceRepr {
485 tx_hash: Option<String>,
486 from_addr: String,
487 to_addr: Option<String>,
488 value: String,
489 input_data: String,
490 gas_used: u64,
491 output: String,
492 success: bool,
493 error: Option<String>,
494 debug_trace_available: bool,
495 contract_address: Option<String>,
496 #[serde(default)]
497 backend: Option<String>,
498 #[serde(default)]
499 capabilities: TraceCapabilities,
500 #[serde(default)]
501 artifacts: TraceArtifacts,
502 #[serde(deserialize_with = "deserialize_shared_steps")]
503 steps: Vec<TraceStep>,
504}
505
506fn deserialize_shared_steps<'de, D: Deserializer<'de>>(
509 deserializer: D,
510) -> Result<Vec<TraceStep>, D::Error> {
511 struct SharedSteps;
512
513 impl<'de> Visitor<'de> for SharedSteps {
514 type Value = Vec<TraceStep>;
515
516 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
517 formatter.write_str("an array of trace steps")
518 }
519
520 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
521 let mut steps = Vec::with_capacity(seq.size_hint().unwrap_or(0));
522 let mut words = WordInterner::new();
523 while let Some(mut step) = seq.next_element::<TraceStep>()? {
524 if let Some(previous) = steps.last() {
525 share_snapshot(previous, &mut step);
526 }
527 words.intern_stack(&mut step.snapshot.stack);
530 steps.push(step);
531 }
532 Ok(steps)
533 }
534 }
535
536 deserializer.deserialize_seq(SharedSteps)
537}
538
539fn share_snapshot(previous: &TraceStep, step: &mut TraceStep) {
541 let previous = &previous.snapshot;
542 let current = &mut step.snapshot;
543 if let (Some(previous_memory), Some(current_memory)) = (&previous.memory, ¤t.memory) {
544 if !Arc::ptr_eq(previous_memory, current_memory) && **previous_memory == **current_memory {
545 current.memory = Some(Arc::clone(previous_memory));
546 }
547 }
548 if !Arc::ptr_eq(&previous.storage, ¤t.storage) && *previous.storage == *current.storage {
549 current.storage = Arc::clone(&previous.storage);
550 }
551 if let (Some(previous_data), Some(current_data)) = (&previous.return_data, ¤t.return_data)
552 {
553 if !Arc::ptr_eq(previous_data, current_data) && **previous_data == **current_data {
554 current.return_data = Some(Arc::clone(previous_data));
555 }
556 }
557}
558
559impl From<TransactionTraceRepr> for TransactionTrace {
560 fn from(repr: TransactionTraceRepr) -> Self {
561 Self {
562 tx_hash: repr.tx_hash,
563 from_addr: repr.from_addr,
564 to_addr: repr.to_addr,
565 value: repr.value,
566 input_data: repr.input_data,
567 gas_used: repr.gas_used,
568 output: repr.output,
569 success: repr.success,
570 error: repr.error,
571 debug_trace_available: repr.debug_trace_available,
572 contract_address: repr.contract_address,
573 backend: repr.backend,
574 capabilities: repr.capabilities,
575 artifacts: repr.artifacts,
576 steps: repr.steps,
577 }
578 }
579}
580
581#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
582pub struct TraceCapabilities {
583 #[serde(default)]
584 pub opcode_steps: bool,
585 #[serde(default)]
586 pub stack: bool,
587 #[serde(default)]
588 pub memory: bool,
589 #[serde(default)]
590 pub storage: bool,
591 #[serde(default)]
592 pub storage_diff: bool,
593 #[serde(default)]
594 pub call_trace: bool,
595 #[serde(default)]
596 pub contract_creation: bool,
597 #[serde(default)]
598 pub logs: bool,
599 #[serde(default)]
600 pub revert_data: bool,
601 #[serde(default)]
602 pub gas_details: bool,
603 #[serde(default)]
604 pub account_changes: bool,
605 #[serde(default)]
607 pub transient_storage: bool,
608 #[serde(default)]
610 pub return_data: bool,
611 #[serde(default)]
612 pub notes: Vec<String>,
613}
614
615#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
616pub struct TraceArtifacts {
617 #[serde(default)]
618 pub calls: Vec<ExecutionCall>,
619 #[serde(default)]
620 pub creations: Vec<ContractCreation>,
621 #[serde(default)]
622 pub logs: Vec<ExecutionLog>,
623 #[serde(default)]
624 pub account_changes: Vec<AccountChange>,
625 #[serde(default)]
626 pub gas: Option<GasSummary>,
627 #[serde(default)]
628 pub revert_data: Option<String>,
629 #[serde(default, skip_serializing_if = "Vec::is_empty")]
632 pub root_kinds: Vec<RootKind>,
633}
634
635#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
637pub struct RootKind {
638 pub entry_step: usize,
639 pub kind: String,
640}
641
642#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
643pub struct ExecutionCall {
644 pub id: usize,
645 pub parent_id: Option<usize>,
646 pub depth: u64,
647 #[serde(default, skip_serializing_if = "Option::is_none")]
648 pub entry_step: Option<usize>,
649 #[serde(default, skip_serializing_if = "Option::is_none")]
650 pub exit_step: Option<usize>,
651 pub call_type: String,
652 pub from: String,
653 pub to: String,
654 pub bytecode_address: String,
655 pub value: String,
656 pub input: String,
657 pub gas_limit: u64,
658 pub gas_used: Option<u64>,
659 pub output: Option<String>,
660 pub success: Option<bool>,
661 pub error: Option<String>,
662}
663
664#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
665pub struct ContractCreation {
666 pub id: usize,
667 pub parent_id: Option<usize>,
668 pub depth: u64,
669 #[serde(default, skip_serializing_if = "Option::is_none")]
670 pub entry_step: Option<usize>,
671 #[serde(default, skip_serializing_if = "Option::is_none")]
672 pub exit_step: Option<usize>,
673 pub create_type: String,
674 pub caller: String,
675 pub address: Option<String>,
676 pub value: String,
677 pub init_code: String,
678 pub gas_limit: u64,
679 pub gas_used: Option<u64>,
680 pub output: Option<String>,
681 pub success: Option<bool>,
682 pub error: Option<String>,
683}
684
685#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
686pub struct ExecutionLog {
687 pub index: usize,
688 pub depth: u64,
689 pub address: String,
690 pub topics: Vec<String>,
691 pub data: String,
692}
693
694#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
695pub struct AccountChange {
696 pub depth: u64,
697 pub kind: String,
698 pub address: Option<String>,
699 pub from: Option<String>,
700 pub to: Option<String>,
701 pub value: Option<String>,
702 pub key: Option<String>,
703 pub previous_value: Option<String>,
704 pub previous_nonce: Option<u64>,
705}
706
707#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
708pub struct GasSummary {
709 pub used: u64,
710 pub spent: Option<u64>,
711 pub refunded: Option<u64>,
712 pub remaining: Option<u64>,
713 pub limit: Option<u64>,
714}
715
716#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
717pub struct FunctionCall {
718 pub name: String,
719 pub selector: String,
720 pub entry_step: usize,
721 pub exit_step: Option<usize>,
722 pub gas_used: u64,
723 pub depth: u64,
724 pub call_type: String,
725 pub contract_address: Option<String>,
726}
727
728#[cfg(test)]
729mod tests {
730 #[test]
733 fn repeated_stack_words_are_stored_once() {
734 let mut words = super::WordInterner::new();
735 assert!(words.is_empty());
736 let first = words.intern("0x2a");
737 let again = words.intern("0x2a");
738 let other = words.intern("0x1");
739 assert_eq!(words.len(), 2);
740 assert_eq!(&*first, "0x2a");
741 assert!(Arc::ptr_eq(&first, &again));
745 assert!(!Arc::ptr_eq(&first, &other));
746
747 let mut stack = vec![Arc::from("0x2a"), Arc::from("0x2a"), Arc::from("0x1")];
748 words.intern_stack(&mut stack);
749 assert!(Arc::ptr_eq(&stack[0], &stack[1]));
750 assert!(Arc::ptr_eq(&stack[0], &first));
751 assert_eq!(words.len(), 2, "nothing new was seen");
752 }
753
754 #[test]
755 fn a_parsed_trace_shares_its_repeated_stack_words() {
756 let document = serde_json::json!({
758 "tx_hash": "0xabc",
759 "from_addr": "0x1",
760 "to_addr": "0x2",
761 "value": "0x0",
762 "input_data": "0x",
763 "gas_used": 0,
764 "output": "0x",
765 "success": true,
766 "debug_trace_available": true,
767 "steps": [
768 {"pc": 0, "op": "PUSH1", "gas": 1, "gas_cost": 1, "depth": 1,
769 "stack": ["0x2a", "0x1"]},
770 {"pc": 2, "op": "ADD", "gas": 0, "gas_cost": 1, "depth": 1,
771 "stack": ["0x2a", "0x1"]}
772 ]
773 })
774 .to_string();
775 let trace: TransactionTrace = serde_json::from_str(&document).expect("trace");
776 let first = &trace.steps[0].snapshot.stack;
777 let second = &trace.steps[1].snapshot.stack;
778 assert_eq!(words(first), ["0x2a", "0x1"]);
779 assert!(Arc::ptr_eq(&first[0], &second[0]));
780 assert!(Arc::ptr_eq(&first[1], &second[1]));
781 }
782 fn words(stack: &[super::Word]) -> Vec<&str> {
783 stack.iter().map(|word| &**word).collect()
784 }
785
786 use std::collections::BTreeMap;
787 use std::sync::Arc;
788
789 use super::{
790 FunctionCall, StepSnapshot, StorageChange, TraceArtifacts, TraceCapabilities, TraceStep,
791 TransactionTrace,
792 };
793
794 fn step_with(snapshot: StepSnapshot) -> TraceStep {
795 TraceStep {
796 pc: 3,
797 op: Arc::from("SSTORE"),
798 gas: 100,
799 gas_cost: 5,
800 depth: 1,
801 stack: vec![Arc::from("0x01"), Arc::from("0x02")],
802 memory: Some("aabb".to_owned()),
803 storage: Some(BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())])),
804 error: None,
805 snapshot,
806 }
807 }
808
809 #[test]
810 fn borrowed_snapshot_matches_the_owned_one() {
811 let flat = step_with(StepSnapshot::default());
816 let populated = step_with(StepSnapshot::new(
817 vec![Arc::from("0x09")],
818 Some("ccdd".to_owned()),
819 BTreeMap::from([("0x01".to_owned(), "0x63".to_owned())]),
820 BTreeMap::from([(
821 "0x01".to_owned(),
822 StorageChange {
823 before: None,
824 after: Some("0x63".to_owned()),
825 },
826 )]),
827 ));
828
829 for step in [&flat, &populated] {
830 assert_eq!(
831 step.snapshot_ref().to_owned_snapshot(),
832 step.normalized_snapshot()
833 );
834 assert_eq!(
835 serde_json::to_string(&step.snapshot_ref()).expect("borrowed snapshot"),
836 serde_json::to_string(&step.normalized_snapshot()).expect("owned snapshot"),
837 );
838 }
839
840 assert_eq!(words(flat.snapshot_ref().stack), ["0x01", "0x02"]);
842 assert_eq!(flat.snapshot_ref().memory, Some("aabb"));
843 assert!(flat.snapshot_ref().storage_diff.is_empty());
844
845 assert_eq!(words(populated.snapshot_ref().stack), ["0x09"]);
847 assert_eq!(populated.snapshot_ref().memory, Some("ccdd"));
848 assert_eq!(populated.snapshot_ref().storage_diff.len(), 1);
849 }
850
851 #[test]
852 fn steps_serialize_in_the_wire_format_and_read_back_stored_once() {
853 let step = TraceStep::new(
856 3,
857 Arc::from("SSTORE"),
858 100,
859 5,
860 1,
861 None,
862 StepSnapshot::new(
863 vec![Arc::from("0x01")],
864 Some("aabb".to_owned()),
865 BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())]),
866 BTreeMap::new(),
867 ),
868 );
869 let json: serde_json::Value = serde_json::to_value(&step).expect("json");
870 assert_eq!(json["stack"], serde_json::json!(["0x01"]));
871 assert_eq!(json["memory"], "aabb");
872 assert_eq!(json["storage"]["0x00"], "0x2a");
873 assert_eq!(json["snapshot"]["memory"], "aabb");
874 assert_eq!(json["snapshot"]["storage"]["0x00"], "0x2a");
875 assert_eq!(json["gas_cost"], 5);
876
877 let flat = step_with(StepSnapshot::default());
879 let flat_json: serde_json::Value = serde_json::to_value(&flat).expect("json");
880 assert_eq!(
881 flat_json["snapshot"]["stack"],
882 serde_json::json!(["0x01", "0x02"])
883 );
884 assert_eq!(flat_json["stack"], serde_json::json!(["0x01", "0x02"]));
885
886 let restored: TraceStep = serde_json::from_value(json).expect("step");
889 assert_eq!(restored, step);
890 assert!(
891 restored.stack.is_empty() && restored.memory.is_none() && restored.storage.is_none()
892 );
893 let legacy: TraceStep = serde_json::from_str(
894 r#"{"pc":1,"op":"ADD","gas":9,"gas_cost":3,"depth":1,"stack":["0x1"],"memory":"cc","storage":{"0x0":"0x1"},"error":null}"#,
895 )
896 .expect("legacy step");
897 assert_eq!(words(legacy.snapshot_ref().stack), ["0x1"]);
898 assert_eq!(legacy.snapshot_ref().memory, Some("cc"));
899 assert_eq!(legacy.snapshot_ref().storage["0x0"], "0x1");
900 assert!(legacy.stack.is_empty());
901 assert_eq!(legacy, step_with_legacy_fields());
902
903 let shared = TraceStep::new(
905 4,
906 Arc::from("POP"),
907 95,
908 2,
909 1,
910 None,
911 StepSnapshot {
912 stack: Vec::new(),
913 memory: step.snapshot.memory.clone(),
914 storage: Arc::clone(&step.snapshot.storage),
915 storage_diff: BTreeMap::new(),
916 transient_storage: BTreeMap::new(),
917 return_data: None,
918 },
919 );
920 assert!(Arc::ptr_eq(
921 &shared.snapshot.storage,
922 &step.snapshot.storage
923 ));
924 assert_eq!(shared.snapshot_ref().memory, Some("aabb"));
925 }
926
927 #[test]
928 fn transient_storage_is_written_only_where_a_step_touched_it() {
929 let plain = TraceStep::new(
930 0,
931 Arc::from("ADD"),
932 1,
933 1,
934 1,
935 None,
936 StepSnapshot::new(
937 vec![Arc::from("0x1")],
938 None,
939 BTreeMap::new(),
940 BTreeMap::new(),
941 ),
942 );
943 let json = serde_json::to_value(&plain).expect("json");
944 assert!(json["snapshot"].get("transient_storage").is_none());
945
946 let touched = TraceStep::new(
947 1,
948 Arc::from("TSTORE"),
949 1,
950 100,
951 1,
952 None,
953 StepSnapshot::new(
954 vec![Arc::from("0x1")],
955 None,
956 BTreeMap::new(),
957 BTreeMap::new(),
958 )
959 .with_transient_storage(BTreeMap::from([("0x0".to_owned(), "0x2".to_owned())])),
960 );
961 let json = serde_json::to_value(&touched).expect("json");
962 assert_eq!(json["snapshot"]["transient_storage"]["0x0"], "0x2");
963 assert_eq!(
964 serde_json::to_value(touched.snapshot_ref()).expect("borrowed"),
965 json["snapshot"]
966 );
967 let restored: TraceStep = serde_json::from_value(json).expect("step");
968 assert_eq!(restored, touched);
969 assert_eq!(restored.snapshot_ref().transient_storage["0x0"], "0x2");
970
971 let capabilities: TraceCapabilities =
974 serde_json::from_str(r#"{"stack":true}"#).expect("capabilities");
975 assert!(!capabilities.transient_storage);
976 }
977
978 #[test]
979 fn return_data_is_written_only_where_the_buffer_holds_some() {
980 let snapshot = || {
981 StepSnapshot::new(
982 vec![Arc::from("0x1")],
983 None,
984 BTreeMap::new(),
985 BTreeMap::new(),
986 )
987 };
988 let empty = TraceStep::new(
989 0,
990 Arc::from("CALL"),
991 1,
992 1,
993 1,
994 None,
995 snapshot().with_return_data(Some(String::new())),
996 );
997 assert!(empty.snapshot.return_data.is_none());
998 let json = serde_json::to_value(&empty).expect("json");
999 assert!(json["snapshot"].get("return_data").is_none());
1000
1001 let filled = TraceStep::new(
1002 1,
1003 Arc::from("RETURNDATASIZE"),
1004 1,
1005 2,
1006 1,
1007 None,
1008 snapshot().with_return_data(Some("2a".to_owned())),
1009 );
1010 let json = serde_json::to_value(&filled).expect("json");
1011 assert_eq!(json["snapshot"]["return_data"], "2a");
1012 assert_eq!(
1013 serde_json::to_value(filled.snapshot_ref()).expect("borrowed"),
1014 json["snapshot"]
1015 );
1016 let restored: TraceStep = serde_json::from_value(json).expect("step");
1017 assert_eq!(restored, filled);
1018 assert_eq!(restored.snapshot_ref().return_data, Some("2a"));
1019
1020 let mut trace = TransactionTrace {
1022 tx_hash: None,
1023 from_addr: "0x1".to_owned(),
1024 to_addr: None,
1025 value: "0x0".to_owned(),
1026 input_data: "0x".to_owned(),
1027 gas_used: 0,
1028 output: "0x".to_owned(),
1029 success: true,
1030 error: None,
1031 debug_trace_available: true,
1032 contract_address: None,
1033 backend: None,
1034 capabilities: TraceCapabilities::default(),
1035 artifacts: TraceArtifacts::default(),
1036 steps: vec![filled.clone(), filled],
1037 };
1038 trace = serde_json::from_str(&serde_json::to_string(&trace).expect("json")).expect("trace");
1039 let data = |index: usize| {
1040 trace.steps[index]
1041 .snapshot
1042 .return_data
1043 .as_ref()
1044 .expect("data")
1045 };
1046 assert!(Arc::ptr_eq(data(0), data(1)));
1047 assert!(!trace.capabilities.return_data);
1048 }
1049
1050 fn step_with_legacy_fields() -> TraceStep {
1051 TraceStep {
1052 pc: 1,
1053 op: Arc::from("ADD"),
1054 gas: 9,
1055 gas_cost: 3,
1056 depth: 1,
1057 stack: vec![Arc::from("0x1")],
1058 memory: Some("cc".to_owned()),
1059 storage: Some(BTreeMap::from([("0x0".to_owned(), "0x1".to_owned())])),
1060 error: None,
1061 snapshot: StepSnapshot::default(),
1062 }
1063 }
1064
1065 #[test]
1066 fn reading_a_trace_back_shares_unchanged_state_between_steps() {
1067 let snapshot = |memory: &str, slot: &str| {
1068 StepSnapshot::new(
1069 Vec::new(),
1070 Some(memory.to_owned()),
1071 BTreeMap::from([("0x0".to_owned(), slot.to_owned())]),
1072 BTreeMap::new(),
1073 )
1074 };
1075 let step = |pc: u64, snapshot: StepSnapshot| {
1076 TraceStep::new(pc, Arc::from("PUSH1"), 1, 1, 1, None, snapshot)
1077 };
1078 let trace = TransactionTrace {
1079 tx_hash: None,
1080 from_addr: "0x1".to_owned(),
1081 to_addr: None,
1082 value: "0x0".to_owned(),
1083 input_data: "0x".to_owned(),
1084 gas_used: 0,
1085 output: "0x".to_owned(),
1086 success: true,
1087 error: None,
1088 debug_trace_available: true,
1089 contract_address: None,
1090 backend: None,
1091 capabilities: TraceCapabilities::default(),
1092 artifacts: TraceArtifacts::default(),
1093 steps: vec![
1094 step(0, snapshot("aa", "0x1")),
1095 step(1, snapshot("aa", "0x1")),
1096 step(2, snapshot("bb", "0x1")),
1097 step(3, snapshot("bb", "0x2")),
1098 ],
1099 };
1100 let json = serde_json::to_string(&trace).expect("json");
1101 let restored: TransactionTrace = serde_json::from_str(&json).expect("trace");
1102 assert_eq!(restored, trace);
1103 let memory = |index: usize| {
1104 restored.steps[index]
1105 .snapshot
1106 .memory
1107 .as_ref()
1108 .expect("memory")
1109 };
1110 assert!(Arc::ptr_eq(memory(0), memory(1)));
1111 assert!(!Arc::ptr_eq(memory(1), memory(2)));
1112 assert!(Arc::ptr_eq(memory(2), memory(3)));
1113 let storage = |index: usize| &restored.steps[index].snapshot.storage;
1114 assert!(Arc::ptr_eq(storage(0), storage(2)));
1115 assert!(!Arc::ptr_eq(storage(2), storage(3)));
1116
1117 let mut by_hand = trace.clone();
1119 assert!(!Arc::ptr_eq(
1120 &by_hand.steps[0].snapshot.storage,
1121 &by_hand.steps[1].snapshot.storage
1122 ));
1123 by_hand.share_unchanged_state();
1124 assert!(Arc::ptr_eq(
1125 &by_hand.steps[0].snapshot.storage,
1126 &by_hand.steps[1].snapshot.storage
1127 ));
1128 }
1129
1130 #[test]
1131 fn core_models_are_serializable() {
1132 let trace = TransactionTrace {
1133 tx_hash: Some("0xabc".to_owned()),
1134 from_addr: "0x1".to_owned(),
1135 to_addr: Some("0x2".to_owned()),
1136 value: "0".to_owned(),
1137 input_data: "0x".to_owned(),
1138 gas_used: 1,
1139 output: "0x".to_owned(),
1140 success: true,
1141 error: None,
1142 steps: vec![TraceStep {
1143 pc: 0,
1144 op: Arc::from("STOP"),
1145 gas: 1,
1146 gas_cost: 0,
1147 depth: 0,
1148 stack: Vec::new(),
1149 memory: None,
1150 storage: None,
1151 error: None,
1152 snapshot: StepSnapshot::default(),
1153 }],
1154 debug_trace_available: true,
1155 contract_address: None,
1156 backend: Some("debug-rpc".to_owned()),
1157 capabilities: TraceCapabilities::default(),
1158 artifacts: TraceArtifacts::default(),
1159 };
1160
1161 let encoded = serde_json::to_string(&trace).expect("trace serializes");
1162 assert!(encoded.contains("\"success\":true"));
1163
1164 let call = FunctionCall {
1165 name: "runtime_dispatcher".to_owned(),
1166 selector: String::new(),
1167 entry_step: 0,
1168 exit_step: Some(0),
1169 gas_used: 1,
1170 depth: 0,
1171 call_type: "entry".to_owned(),
1172 contract_address: Some("0x2".to_owned()),
1173 };
1174 assert_eq!(call.call_type, "entry");
1175
1176 let change = StorageChange {
1177 before: None,
1178 after: Some("0x2a".to_owned()),
1179 };
1180 assert_eq!(change.after.as_deref(), Some("0x2a"));
1181 }
1182}