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 };
119 }
120 StepSnapshotRef {
121 stack: &self.stack,
122 memory: self.memory.as_deref(),
123 storage: self.storage.as_ref().unwrap_or(&EMPTY_STORAGE),
124 storage_diff: &EMPTY_STORAGE_DIFF,
125 }
126 }
127
128 #[must_use]
133 pub fn normalized_snapshot(&self) -> StepSnapshot {
134 self.snapshot_ref().to_owned_snapshot()
135 }
136
137 fn normalized(mut self) -> Self {
140 if self.snapshot.is_empty() {
141 self.snapshot = StepSnapshot {
142 stack: std::mem::take(&mut self.stack),
143 memory: self.memory.take().map(Arc::from),
144 storage: Arc::new(self.storage.take().unwrap_or_default()),
145 storage_diff: BTreeMap::new(),
146 };
147 } else {
148 self.stack = Vec::new();
149 self.memory = None;
150 self.storage = None;
151 }
152 self
153 }
154}
155
156impl PartialEq for TraceStep {
157 fn eq(&self, other: &Self) -> bool {
159 self.pc == other.pc
160 && self.op == other.op
161 && self.gas == other.gas
162 && self.gas_cost == other.gas_cost
163 && self.depth == other.depth
164 && self.error == other.error
165 && self.snapshot_ref() == other.snapshot_ref()
166 }
167}
168
169impl Serialize for TraceStep {
170 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
173 let snapshot = self.snapshot_ref();
174 let mut state = serializer.serialize_struct("TraceStep", 10)?;
175 state.serialize_field("pc", &self.pc)?;
176 state.serialize_field("op", &self.op)?;
177 state.serialize_field("gas", &self.gas)?;
178 state.serialize_field("gas_cost", &self.gas_cost)?;
179 state.serialize_field("depth", &self.depth)?;
180 state.serialize_field("stack", snapshot.stack)?;
181 state.serialize_field("memory", &snapshot.memory)?;
182 state.serialize_field("storage", snapshot.storage)?;
183 state.serialize_field("error", &self.error)?;
184 state.serialize_field("snapshot", &snapshot)?;
185 state.end()
186 }
187}
188
189#[derive(Deserialize)]
191struct TraceStepRepr {
192 pc: u64,
193 op: String,
194 gas: u64,
195 gas_cost: u64,
196 depth: u64,
197 #[serde(default)]
198 stack: Vec<String>,
199 #[serde(default)]
200 memory: Option<String>,
201 #[serde(default)]
202 storage: Option<BTreeMap<String, String>>,
203 #[serde(default)]
204 error: Option<String>,
205 #[serde(default)]
206 snapshot: StepSnapshot,
207}
208
209impl<'de> Deserialize<'de> for TraceStep {
210 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
211 let repr = TraceStepRepr::deserialize(deserializer)?;
212 Ok(TraceStep {
213 pc: repr.pc,
214 op: Arc::from(repr.op),
215 gas: repr.gas,
216 gas_cost: repr.gas_cost,
217 depth: repr.depth,
218 stack: repr.stack.into_iter().map(Arc::from).collect(),
219 memory: repr.memory,
220 storage: repr.storage,
221 error: repr.error,
222 snapshot: repr.snapshot,
223 }
224 .normalized())
225 }
226}
227
228pub type Word = Arc<str>;
237
238#[derive(Debug, Default)]
243pub struct WordInterner {
244 words: std::collections::HashMap<Word, Word>,
245}
246
247impl WordInterner {
248 #[must_use]
249 pub fn new() -> Self {
250 Self::default()
251 }
252
253 #[must_use]
255 pub fn intern(&mut self, word: &str) -> Word {
256 if let Some(shared) = self.words.get(word) {
257 return Arc::clone(shared);
258 }
259 let shared: Word = Arc::from(word);
260 self.words.insert(Arc::clone(&shared), Arc::clone(&shared));
261 shared
262 }
263
264 pub fn intern_stack(&mut self, stack: &mut [Word]) {
266 for word in stack {
267 *word = self.intern(word);
268 }
269 }
270
271 #[must_use]
273 pub fn len(&self) -> usize {
274 self.words.len()
275 }
276
277 #[must_use]
278 pub fn is_empty(&self) -> bool {
279 self.words.is_empty()
280 }
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
288pub struct StepSnapshotRef<'a> {
289 pub stack: &'a [Word],
290 pub memory: Option<&'a str>,
291 pub storage: &'a BTreeMap<String, String>,
292 pub storage_diff: &'a BTreeMap<String, StorageChange>,
293}
294
295impl StepSnapshotRef<'_> {
296 #[must_use]
298 pub fn to_owned_snapshot(&self) -> StepSnapshot {
299 StepSnapshot {
300 stack: self.stack.to_vec(),
301 memory: self.memory.map(Arc::from),
302 storage: Arc::new(self.storage.clone()),
303 storage_diff: self.storage_diff.clone(),
304 }
305 }
306}
307
308#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
315#[serde(from = "StepSnapshotRepr")]
316pub struct StepSnapshot {
317 pub stack: Vec<Word>,
318 pub memory: Option<Arc<str>>,
320 pub storage: Arc<BTreeMap<String, String>>,
321 pub storage_diff: BTreeMap<String, StorageChange>,
322}
323
324impl StepSnapshot {
325 #[must_use]
327 pub fn new(
328 stack: Vec<Word>,
329 memory: Option<String>,
330 storage: BTreeMap<String, String>,
331 storage_diff: BTreeMap<String, StorageChange>,
332 ) -> Self {
333 Self {
334 stack,
335 memory: memory.map(Arc::from),
336 storage: Arc::new(storage),
337 storage_diff,
338 }
339 }
340
341 #[must_use]
342 pub fn is_empty(&self) -> bool {
343 self.stack.is_empty()
344 && self.memory.is_none()
345 && self.storage.is_empty()
346 && self.storage_diff.is_empty()
347 }
348}
349
350impl Serialize for StepSnapshot {
351 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
352 let mut state = serializer.serialize_struct("StepSnapshot", 4)?;
353 state.serialize_field("stack", &self.stack)?;
354 state.serialize_field("memory", &self.memory.as_deref())?;
355 state.serialize_field("storage", &*self.storage)?;
356 state.serialize_field("storage_diff", &self.storage_diff)?;
357 state.end()
358 }
359}
360
361#[derive(Deserialize, Default)]
362struct StepSnapshotRepr {
363 #[serde(default)]
364 stack: Vec<Word>,
365 #[serde(default)]
366 memory: Option<String>,
367 #[serde(default)]
368 storage: BTreeMap<String, String>,
369 #[serde(default)]
370 storage_diff: BTreeMap<String, StorageChange>,
371}
372
373impl From<StepSnapshotRepr> for StepSnapshot {
374 fn from(repr: StepSnapshotRepr) -> Self {
375 Self::new(repr.stack, repr.memory, repr.storage, repr.storage_diff)
376 }
377}
378
379#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
380pub struct StorageChange {
381 pub before: Option<String>,
382 pub after: Option<String>,
383}
384
385#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
392#[serde(from = "TransactionTraceRepr")]
393pub struct TransactionTrace {
394 pub tx_hash: Option<String>,
395 pub from_addr: String,
396 pub to_addr: Option<String>,
397 pub value: String,
398 pub input_data: String,
399 pub gas_used: u64,
400 pub output: String,
401 pub success: bool,
402 pub error: Option<String>,
403 pub debug_trace_available: bool,
404 pub contract_address: Option<String>,
405 #[serde(default)]
406 pub backend: Option<String>,
407 #[serde(default)]
408 pub capabilities: TraceCapabilities,
409 #[serde(default)]
410 pub artifacts: TraceArtifacts,
411 pub steps: Vec<TraceStep>,
412}
413
414impl TransactionTrace {
415 pub fn share_unchanged_state(&mut self) {
420 for index in 1..self.steps.len() {
421 let (before, after) = self.steps.split_at_mut(index);
422 share_snapshot(&before[index - 1], &mut after[0]);
423 }
424 }
425}
426
427#[derive(Deserialize)]
429struct TransactionTraceRepr {
430 tx_hash: Option<String>,
431 from_addr: String,
432 to_addr: Option<String>,
433 value: String,
434 input_data: String,
435 gas_used: u64,
436 output: String,
437 success: bool,
438 error: Option<String>,
439 debug_trace_available: bool,
440 contract_address: Option<String>,
441 #[serde(default)]
442 backend: Option<String>,
443 #[serde(default)]
444 capabilities: TraceCapabilities,
445 #[serde(default)]
446 artifacts: TraceArtifacts,
447 #[serde(deserialize_with = "deserialize_shared_steps")]
448 steps: Vec<TraceStep>,
449}
450
451fn deserialize_shared_steps<'de, D: Deserializer<'de>>(
454 deserializer: D,
455) -> Result<Vec<TraceStep>, D::Error> {
456 struct SharedSteps;
457
458 impl<'de> Visitor<'de> for SharedSteps {
459 type Value = Vec<TraceStep>;
460
461 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
462 formatter.write_str("an array of trace steps")
463 }
464
465 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
466 let mut steps = Vec::with_capacity(seq.size_hint().unwrap_or(0));
467 let mut words = WordInterner::new();
468 while let Some(mut step) = seq.next_element::<TraceStep>()? {
469 if let Some(previous) = steps.last() {
470 share_snapshot(previous, &mut step);
471 }
472 words.intern_stack(&mut step.snapshot.stack);
475 steps.push(step);
476 }
477 Ok(steps)
478 }
479 }
480
481 deserializer.deserialize_seq(SharedSteps)
482}
483
484fn share_snapshot(previous: &TraceStep, step: &mut TraceStep) {
486 let previous = &previous.snapshot;
487 let current = &mut step.snapshot;
488 if let (Some(previous_memory), Some(current_memory)) = (&previous.memory, ¤t.memory) {
489 if !Arc::ptr_eq(previous_memory, current_memory) && **previous_memory == **current_memory {
490 current.memory = Some(Arc::clone(previous_memory));
491 }
492 }
493 if !Arc::ptr_eq(&previous.storage, ¤t.storage) && *previous.storage == *current.storage {
494 current.storage = Arc::clone(&previous.storage);
495 }
496}
497
498impl From<TransactionTraceRepr> for TransactionTrace {
499 fn from(repr: TransactionTraceRepr) -> Self {
500 Self {
501 tx_hash: repr.tx_hash,
502 from_addr: repr.from_addr,
503 to_addr: repr.to_addr,
504 value: repr.value,
505 input_data: repr.input_data,
506 gas_used: repr.gas_used,
507 output: repr.output,
508 success: repr.success,
509 error: repr.error,
510 debug_trace_available: repr.debug_trace_available,
511 contract_address: repr.contract_address,
512 backend: repr.backend,
513 capabilities: repr.capabilities,
514 artifacts: repr.artifacts,
515 steps: repr.steps,
516 }
517 }
518}
519
520#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
521pub struct TraceCapabilities {
522 #[serde(default)]
523 pub opcode_steps: bool,
524 #[serde(default)]
525 pub stack: bool,
526 #[serde(default)]
527 pub memory: bool,
528 #[serde(default)]
529 pub storage: bool,
530 #[serde(default)]
531 pub storage_diff: bool,
532 #[serde(default)]
533 pub call_trace: bool,
534 #[serde(default)]
535 pub contract_creation: bool,
536 #[serde(default)]
537 pub logs: bool,
538 #[serde(default)]
539 pub revert_data: bool,
540 #[serde(default)]
541 pub gas_details: bool,
542 #[serde(default)]
543 pub account_changes: bool,
544 #[serde(default)]
545 pub notes: Vec<String>,
546}
547
548#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
549pub struct TraceArtifacts {
550 #[serde(default)]
551 pub calls: Vec<ExecutionCall>,
552 #[serde(default)]
553 pub creations: Vec<ContractCreation>,
554 #[serde(default)]
555 pub logs: Vec<ExecutionLog>,
556 #[serde(default)]
557 pub account_changes: Vec<AccountChange>,
558 #[serde(default)]
559 pub gas: Option<GasSummary>,
560 #[serde(default)]
561 pub revert_data: Option<String>,
562}
563
564#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
565pub struct ExecutionCall {
566 pub id: usize,
567 pub parent_id: Option<usize>,
568 pub depth: u64,
569 #[serde(default, skip_serializing_if = "Option::is_none")]
570 pub entry_step: Option<usize>,
571 #[serde(default, skip_serializing_if = "Option::is_none")]
572 pub exit_step: Option<usize>,
573 pub call_type: String,
574 pub from: String,
575 pub to: String,
576 pub bytecode_address: String,
577 pub value: String,
578 pub input: String,
579 pub gas_limit: u64,
580 pub gas_used: Option<u64>,
581 pub output: Option<String>,
582 pub success: Option<bool>,
583 pub error: Option<String>,
584}
585
586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587pub struct ContractCreation {
588 pub id: usize,
589 pub parent_id: Option<usize>,
590 pub depth: u64,
591 #[serde(default, skip_serializing_if = "Option::is_none")]
592 pub entry_step: Option<usize>,
593 #[serde(default, skip_serializing_if = "Option::is_none")]
594 pub exit_step: Option<usize>,
595 pub create_type: String,
596 pub caller: String,
597 pub address: Option<String>,
598 pub value: String,
599 pub init_code: String,
600 pub gas_limit: u64,
601 pub gas_used: Option<u64>,
602 pub output: Option<String>,
603 pub success: Option<bool>,
604 pub error: Option<String>,
605}
606
607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
608pub struct ExecutionLog {
609 pub index: usize,
610 pub depth: u64,
611 pub address: String,
612 pub topics: Vec<String>,
613 pub data: String,
614}
615
616#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
617pub struct AccountChange {
618 pub depth: u64,
619 pub kind: String,
620 pub address: Option<String>,
621 pub from: Option<String>,
622 pub to: Option<String>,
623 pub value: Option<String>,
624 pub key: Option<String>,
625 pub previous_value: Option<String>,
626 pub previous_nonce: Option<u64>,
627}
628
629#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
630pub struct GasSummary {
631 pub used: u64,
632 pub spent: Option<u64>,
633 pub refunded: Option<u64>,
634 pub remaining: Option<u64>,
635 pub limit: Option<u64>,
636}
637
638#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
639pub struct FunctionCall {
640 pub name: String,
641 pub selector: String,
642 pub entry_step: usize,
643 pub exit_step: Option<usize>,
644 pub gas_used: u64,
645 pub depth: u64,
646 pub call_type: String,
647 pub contract_address: Option<String>,
648}
649
650#[cfg(test)]
651mod tests {
652 #[test]
655 fn repeated_stack_words_are_stored_once() {
656 let mut words = super::WordInterner::new();
657 assert!(words.is_empty());
658 let first = words.intern("0x2a");
659 let again = words.intern("0x2a");
660 let other = words.intern("0x1");
661 assert_eq!(words.len(), 2);
662 assert_eq!(&*first, "0x2a");
663 assert!(Arc::ptr_eq(&first, &again));
667 assert!(!Arc::ptr_eq(&first, &other));
668
669 let mut stack = vec![Arc::from("0x2a"), Arc::from("0x2a"), Arc::from("0x1")];
670 words.intern_stack(&mut stack);
671 assert!(Arc::ptr_eq(&stack[0], &stack[1]));
672 assert!(Arc::ptr_eq(&stack[0], &first));
673 assert_eq!(words.len(), 2, "nothing new was seen");
674 }
675
676 #[test]
677 fn a_parsed_trace_shares_its_repeated_stack_words() {
678 let document = serde_json::json!({
680 "tx_hash": "0xabc",
681 "from_addr": "0x1",
682 "to_addr": "0x2",
683 "value": "0x0",
684 "input_data": "0x",
685 "gas_used": 0,
686 "output": "0x",
687 "success": true,
688 "debug_trace_available": true,
689 "steps": [
690 {"pc": 0, "op": "PUSH1", "gas": 1, "gas_cost": 1, "depth": 1,
691 "stack": ["0x2a", "0x1"]},
692 {"pc": 2, "op": "ADD", "gas": 0, "gas_cost": 1, "depth": 1,
693 "stack": ["0x2a", "0x1"]}
694 ]
695 })
696 .to_string();
697 let trace: TransactionTrace = serde_json::from_str(&document).expect("trace");
698 let first = &trace.steps[0].snapshot.stack;
699 let second = &trace.steps[1].snapshot.stack;
700 assert_eq!(words(first), ["0x2a", "0x1"]);
701 assert!(Arc::ptr_eq(&first[0], &second[0]));
702 assert!(Arc::ptr_eq(&first[1], &second[1]));
703 }
704 fn words(stack: &[super::Word]) -> Vec<&str> {
705 stack.iter().map(|word| &**word).collect()
706 }
707
708 use std::collections::BTreeMap;
709 use std::sync::Arc;
710
711 use super::{
712 FunctionCall, StepSnapshot, StorageChange, TraceArtifacts, TraceCapabilities, TraceStep,
713 TransactionTrace,
714 };
715
716 fn step_with(snapshot: StepSnapshot) -> TraceStep {
717 TraceStep {
718 pc: 3,
719 op: Arc::from("SSTORE"),
720 gas: 100,
721 gas_cost: 5,
722 depth: 1,
723 stack: vec![Arc::from("0x01"), Arc::from("0x02")],
724 memory: Some("aabb".to_owned()),
725 storage: Some(BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())])),
726 error: None,
727 snapshot,
728 }
729 }
730
731 #[test]
732 fn borrowed_snapshot_matches_the_owned_one() {
733 let flat = step_with(StepSnapshot::default());
738 let populated = step_with(StepSnapshot::new(
739 vec![Arc::from("0x09")],
740 Some("ccdd".to_owned()),
741 BTreeMap::from([("0x01".to_owned(), "0x63".to_owned())]),
742 BTreeMap::from([(
743 "0x01".to_owned(),
744 StorageChange {
745 before: None,
746 after: Some("0x63".to_owned()),
747 },
748 )]),
749 ));
750
751 for step in [&flat, &populated] {
752 assert_eq!(
753 step.snapshot_ref().to_owned_snapshot(),
754 step.normalized_snapshot()
755 );
756 assert_eq!(
757 serde_json::to_string(&step.snapshot_ref()).expect("borrowed snapshot"),
758 serde_json::to_string(&step.normalized_snapshot()).expect("owned snapshot"),
759 );
760 }
761
762 assert_eq!(words(flat.snapshot_ref().stack), ["0x01", "0x02"]);
764 assert_eq!(flat.snapshot_ref().memory, Some("aabb"));
765 assert!(flat.snapshot_ref().storage_diff.is_empty());
766
767 assert_eq!(words(populated.snapshot_ref().stack), ["0x09"]);
769 assert_eq!(populated.snapshot_ref().memory, Some("ccdd"));
770 assert_eq!(populated.snapshot_ref().storage_diff.len(), 1);
771 }
772
773 #[test]
774 fn steps_serialize_in_the_wire_format_and_read_back_stored_once() {
775 let step = TraceStep::new(
778 3,
779 Arc::from("SSTORE"),
780 100,
781 5,
782 1,
783 None,
784 StepSnapshot::new(
785 vec![Arc::from("0x01")],
786 Some("aabb".to_owned()),
787 BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())]),
788 BTreeMap::new(),
789 ),
790 );
791 let json: serde_json::Value = serde_json::to_value(&step).expect("json");
792 assert_eq!(json["stack"], serde_json::json!(["0x01"]));
793 assert_eq!(json["memory"], "aabb");
794 assert_eq!(json["storage"]["0x00"], "0x2a");
795 assert_eq!(json["snapshot"]["memory"], "aabb");
796 assert_eq!(json["snapshot"]["storage"]["0x00"], "0x2a");
797 assert_eq!(json["gas_cost"], 5);
798
799 let flat = step_with(StepSnapshot::default());
801 let flat_json: serde_json::Value = serde_json::to_value(&flat).expect("json");
802 assert_eq!(
803 flat_json["snapshot"]["stack"],
804 serde_json::json!(["0x01", "0x02"])
805 );
806 assert_eq!(flat_json["stack"], serde_json::json!(["0x01", "0x02"]));
807
808 let restored: TraceStep = serde_json::from_value(json).expect("step");
811 assert_eq!(restored, step);
812 assert!(
813 restored.stack.is_empty() && restored.memory.is_none() && restored.storage.is_none()
814 );
815 let legacy: TraceStep = serde_json::from_str(
816 r#"{"pc":1,"op":"ADD","gas":9,"gas_cost":3,"depth":1,"stack":["0x1"],"memory":"cc","storage":{"0x0":"0x1"},"error":null}"#,
817 )
818 .expect("legacy step");
819 assert_eq!(words(legacy.snapshot_ref().stack), ["0x1"]);
820 assert_eq!(legacy.snapshot_ref().memory, Some("cc"));
821 assert_eq!(legacy.snapshot_ref().storage["0x0"], "0x1");
822 assert!(legacy.stack.is_empty());
823 assert_eq!(legacy, step_with_legacy_fields());
824
825 let shared = TraceStep::new(
827 4,
828 Arc::from("POP"),
829 95,
830 2,
831 1,
832 None,
833 StepSnapshot {
834 stack: Vec::new(),
835 memory: step.snapshot.memory.clone(),
836 storage: Arc::clone(&step.snapshot.storage),
837 storage_diff: BTreeMap::new(),
838 },
839 );
840 assert!(Arc::ptr_eq(
841 &shared.snapshot.storage,
842 &step.snapshot.storage
843 ));
844 assert_eq!(shared.snapshot_ref().memory, Some("aabb"));
845 }
846
847 fn step_with_legacy_fields() -> TraceStep {
848 TraceStep {
849 pc: 1,
850 op: Arc::from("ADD"),
851 gas: 9,
852 gas_cost: 3,
853 depth: 1,
854 stack: vec![Arc::from("0x1")],
855 memory: Some("cc".to_owned()),
856 storage: Some(BTreeMap::from([("0x0".to_owned(), "0x1".to_owned())])),
857 error: None,
858 snapshot: StepSnapshot::default(),
859 }
860 }
861
862 #[test]
863 fn reading_a_trace_back_shares_unchanged_state_between_steps() {
864 let snapshot = |memory: &str, slot: &str| {
865 StepSnapshot::new(
866 Vec::new(),
867 Some(memory.to_owned()),
868 BTreeMap::from([("0x0".to_owned(), slot.to_owned())]),
869 BTreeMap::new(),
870 )
871 };
872 let step = |pc: u64, snapshot: StepSnapshot| {
873 TraceStep::new(pc, Arc::from("PUSH1"), 1, 1, 1, None, snapshot)
874 };
875 let trace = TransactionTrace {
876 tx_hash: None,
877 from_addr: "0x1".to_owned(),
878 to_addr: None,
879 value: "0x0".to_owned(),
880 input_data: "0x".to_owned(),
881 gas_used: 0,
882 output: "0x".to_owned(),
883 success: true,
884 error: None,
885 debug_trace_available: true,
886 contract_address: None,
887 backend: None,
888 capabilities: TraceCapabilities::default(),
889 artifacts: TraceArtifacts::default(),
890 steps: vec![
891 step(0, snapshot("aa", "0x1")),
892 step(1, snapshot("aa", "0x1")),
893 step(2, snapshot("bb", "0x1")),
894 step(3, snapshot("bb", "0x2")),
895 ],
896 };
897 let json = serde_json::to_string(&trace).expect("json");
898 let restored: TransactionTrace = serde_json::from_str(&json).expect("trace");
899 assert_eq!(restored, trace);
900 let memory = |index: usize| {
901 restored.steps[index]
902 .snapshot
903 .memory
904 .as_ref()
905 .expect("memory")
906 };
907 assert!(Arc::ptr_eq(memory(0), memory(1)));
908 assert!(!Arc::ptr_eq(memory(1), memory(2)));
909 assert!(Arc::ptr_eq(memory(2), memory(3)));
910 let storage = |index: usize| &restored.steps[index].snapshot.storage;
911 assert!(Arc::ptr_eq(storage(0), storage(2)));
912 assert!(!Arc::ptr_eq(storage(2), storage(3)));
913
914 let mut by_hand = trace.clone();
916 assert!(!Arc::ptr_eq(
917 &by_hand.steps[0].snapshot.storage,
918 &by_hand.steps[1].snapshot.storage
919 ));
920 by_hand.share_unchanged_state();
921 assert!(Arc::ptr_eq(
922 &by_hand.steps[0].snapshot.storage,
923 &by_hand.steps[1].snapshot.storage
924 ));
925 }
926
927 #[test]
928 fn core_models_are_serializable() {
929 let trace = TransactionTrace {
930 tx_hash: Some("0xabc".to_owned()),
931 from_addr: "0x1".to_owned(),
932 to_addr: Some("0x2".to_owned()),
933 value: "0".to_owned(),
934 input_data: "0x".to_owned(),
935 gas_used: 1,
936 output: "0x".to_owned(),
937 success: true,
938 error: None,
939 steps: vec![TraceStep {
940 pc: 0,
941 op: Arc::from("STOP"),
942 gas: 1,
943 gas_cost: 0,
944 depth: 0,
945 stack: Vec::new(),
946 memory: None,
947 storage: None,
948 error: None,
949 snapshot: StepSnapshot::default(),
950 }],
951 debug_trace_available: true,
952 contract_address: None,
953 backend: Some("debug-rpc".to_owned()),
954 capabilities: TraceCapabilities::default(),
955 artifacts: TraceArtifacts::default(),
956 };
957
958 let encoded = serde_json::to_string(&trace).expect("trace serializes");
959 assert!(encoded.contains("\"success\":true"));
960
961 let call = FunctionCall {
962 name: "runtime_dispatcher".to_owned(),
963 selector: String::new(),
964 entry_step: 0,
965 exit_step: Some(0),
966 gas_used: 1,
967 depth: 0,
968 call_type: "entry".to_owned(),
969 contract_address: Some("0x2".to_owned()),
970 };
971 assert_eq!(call.call_type, "entry");
972
973 let change = StorageChange {
974 before: None,
975 after: Some("0x2a".to_owned()),
976 };
977 assert_eq!(change.after.as_deref(), Some("0x2a"));
978 }
979}