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