1use std::collections::BTreeMap;
19use std::fmt;
20use std::sync::Arc;
21
22use serde::de::{self, Visitor};
23use serde::ser::SerializeStruct;
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use thiserror::Error;
26
27pub use bytes::Bytes;
28
29mod memory;
30pub mod opcode;
31mod table;
32mod word;
33
34pub use memory::MemoryWriteError;
35pub use table::{Stack, StepError, StepRecord, StepTable, StepView, Steps, STACK_CHECKPOINT};
36pub use word::{EvmWord, WordError};
37
38#[derive(Debug, Error)]
39pub enum SoldbError {
40 #[error("{0}")]
42 Message(String),
43 #[error("")]
49 AlreadyReported,
50}
51
52pub type SoldbResult<T> = Result<T, SoldbError>;
53
54impl From<StepError> for SoldbError {
55 fn from(error: StepError) -> Self {
56 Self::Message(format!("invalid trace step: {error}"))
57 }
58}
59
60#[derive(Debug, Clone, Eq)]
77pub struct TraceStep {
78 pub pc: u64,
79 pub op: Word,
82 pub gas: u64,
83 pub gas_cost: u64,
86 pub depth: u64,
87 pub stack: Vec<Word>,
89 pub memory: Option<Bytes>,
91 pub storage: Option<BTreeMap<String, String>>,
93 pub error: Option<String>,
94 pub snapshot: StepSnapshot,
95}
96
97impl TraceStep {
98 #[must_use]
100 pub fn new(
101 pc: u64,
102 op: Word,
103 gas: u64,
104 gas_cost: u64,
105 depth: u64,
106 error: Option<String>,
107 snapshot: StepSnapshot,
108 ) -> Self {
109 Self {
110 pc,
111 op,
112 gas,
113 gas_cost,
114 depth,
115 stack: Vec::new(),
116 memory: None,
117 storage: None,
118 error,
119 snapshot,
120 }
121 }
122
123 #[must_use]
133 pub fn snapshot_ref(&self) -> StepSnapshotRef<'_> {
134 static EMPTY_STORAGE: BTreeMap<String, String> = BTreeMap::new();
135 static EMPTY_STORAGE_DIFF: BTreeMap<String, StorageChange> = BTreeMap::new();
136
137 if !self.snapshot.is_empty() {
138 return StepSnapshotRef {
139 stack: &self.snapshot.stack,
140 memory: self.snapshot.memory.as_deref(),
141 storage: &self.snapshot.storage,
142 storage_diff: &self.snapshot.storage_diff,
143 transient_storage: &self.snapshot.transient_storage,
144 return_data: self.snapshot.return_data.as_deref(),
145 };
146 }
147 StepSnapshotRef {
148 stack: &self.stack,
149 memory: self.memory.as_deref(),
150 storage: self.storage.as_ref().unwrap_or(&EMPTY_STORAGE),
151 storage_diff: &EMPTY_STORAGE_DIFF,
152 transient_storage: &EMPTY_STORAGE,
153 return_data: None,
154 }
155 }
156
157 #[must_use]
162 pub fn normalized_snapshot(&self) -> StepSnapshot {
163 self.snapshot_ref().to_owned_snapshot()
164 }
165
166 pub(crate) fn normalized(mut self) -> Self {
169 if self.snapshot.is_empty() {
170 self.snapshot = StepSnapshot {
171 stack: std::mem::take(&mut self.stack),
172 memory: self.memory.take(),
173 storage: Arc::new(self.storage.take().unwrap_or_default()),
174 storage_diff: BTreeMap::new(),
175 transient_storage: BTreeMap::new(),
176 return_data: None,
177 };
178 } else {
179 self.stack = Vec::new();
180 self.memory = None;
181 self.storage = None;
182 }
183 self
184 }
185}
186
187impl PartialEq for TraceStep {
188 fn eq(&self, other: &Self) -> bool {
190 self.pc == other.pc
191 && self.op == other.op
192 && self.gas == other.gas
193 && self.gas_cost == other.gas_cost
194 && self.depth == other.depth
195 && self.error == other.error
196 && self.snapshot_ref() == other.snapshot_ref()
197 }
198}
199
200impl Serialize for TraceStep {
201 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
204 let snapshot = self.snapshot_ref();
205 let mut state = serializer.serialize_struct("TraceStep", 10)?;
206 state.serialize_field("pc", &self.pc)?;
207 state.serialize_field("op", &self.op)?;
208 state.serialize_field("gas", &self.gas)?;
209 state.serialize_field("gas_cost", &self.gas_cost)?;
210 state.serialize_field("depth", &self.depth)?;
211 state.serialize_field("stack", snapshot.stack)?;
212 state.serialize_field("memory", &snapshot.memory.map(Hex))?;
213 state.serialize_field("storage", snapshot.storage)?;
214 state.serialize_field("error", &self.error)?;
215 state.serialize_field("snapshot", &snapshot)?;
216 state.end()
217 }
218}
219
220#[derive(Deserialize)]
222struct TraceStepRepr {
223 pc: u64,
224 op: String,
225 gas: u64,
226 gas_cost: u64,
227 depth: u64,
228 #[serde(default)]
229 stack: Vec<String>,
230 #[serde(default, deserialize_with = "deserialize_memory")]
231 memory: Option<Bytes>,
232 #[serde(default)]
233 storage: Option<BTreeMap<String, String>>,
234 #[serde(default)]
235 error: Option<String>,
236 #[serde(default)]
237 snapshot: StepSnapshot,
238}
239
240impl<'de> Deserialize<'de> for TraceStep {
241 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
242 let repr = TraceStepRepr::deserialize(deserializer)?;
243 Ok(TraceStep {
244 pc: repr.pc,
245 op: Arc::from(repr.op),
246 gas: repr.gas,
247 gas_cost: repr.gas_cost,
248 depth: repr.depth,
249 stack: repr.stack.into_iter().map(Arc::from).collect(),
250 memory: repr.memory,
251 storage: repr.storage,
252 error: repr.error,
253 snapshot: repr.snapshot,
254 }
255 .normalized())
256 }
257}
258
259pub type Word = Arc<str>;
268
269#[derive(Debug, Default)]
274pub struct WordInterner {
275 words: std::collections::HashMap<Word, Word>,
276}
277
278impl WordInterner {
279 #[must_use]
280 pub fn new() -> Self {
281 Self::default()
282 }
283
284 #[must_use]
286 pub fn intern(&mut self, word: &str) -> Word {
287 if let Some(shared) = self.words.get(word) {
288 return Arc::clone(shared);
289 }
290 let shared: Word = Arc::from(word);
291 self.words.insert(Arc::clone(&shared), Arc::clone(&shared));
292 shared
293 }
294
295 pub fn intern_stack(&mut self, stack: &mut [Word]) {
297 for word in stack {
298 *word = self.intern(word);
299 }
300 }
301
302 #[must_use]
304 pub fn len(&self) -> usize {
305 self.words.len()
306 }
307
308 #[must_use]
309 pub fn is_empty(&self) -> bool {
310 self.words.is_empty()
311 }
312}
313
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
319pub struct StepSnapshotRef<'a> {
320 pub stack: &'a [Word],
321 #[serde(serialize_with = "serialize_memory")]
323 pub memory: Option<&'a [u8]>,
324 pub storage: &'a BTreeMap<String, String>,
325 pub storage_diff: &'a BTreeMap<String, StorageChange>,
326 #[serde(skip_serializing_if = "is_empty_ref")]
327 pub transient_storage: &'a BTreeMap<String, String>,
328 #[serde(skip_serializing_if = "Option::is_none")]
329 pub return_data: Option<&'a str>,
330}
331
332fn is_empty_ref(map: &&BTreeMap<String, String>) -> bool {
333 map.is_empty()
334}
335
336fn serialize_memory<S: Serializer>(
337 memory: &Option<&[u8]>,
338 serializer: S,
339) -> Result<S::Ok, S::Error> {
340 memory.map(Hex).serialize(serializer)
341}
342
343impl StepSnapshotRef<'_> {
344 #[must_use]
346 pub fn to_owned_snapshot(&self) -> StepSnapshot {
347 StepSnapshot {
348 stack: self.stack.to_vec(),
349 memory: self.memory.map(Bytes::copy_from_slice),
350 storage: Arc::new(self.storage.clone()),
351 storage_diff: self.storage_diff.clone(),
352 transient_storage: self.transient_storage.clone(),
353 return_data: self.return_data.map(Arc::from),
354 }
355 }
356}
357
358#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
369#[serde(from = "StepSnapshotRepr")]
370pub struct StepSnapshot {
371 pub stack: Vec<Word>,
372 pub memory: Option<Bytes>,
375 pub storage: Arc<BTreeMap<String, String>>,
376 pub storage_diff: BTreeMap<String, StorageChange>,
377 pub transient_storage: BTreeMap<String, String>,
379 pub return_data: Option<Arc<str>>,
381}
382
383impl StepSnapshot {
384 #[must_use]
386 pub fn new(
387 stack: Vec<Word>,
388 memory: Option<Bytes>,
389 storage: BTreeMap<String, String>,
390 storage_diff: BTreeMap<String, StorageChange>,
391 ) -> Self {
392 Self {
393 stack,
394 memory,
395 storage: Arc::new(storage),
396 storage_diff,
397 transient_storage: BTreeMap::new(),
398 return_data: None,
399 }
400 }
401
402 #[must_use]
404 pub fn with_transient_storage(mut self, transient_storage: BTreeMap<String, String>) -> Self {
405 self.transient_storage = transient_storage;
406 self
407 }
408
409 #[must_use]
411 pub fn with_return_data(mut self, return_data: Option<String>) -> Self {
412 self.return_data = return_data.filter(|data| !data.is_empty()).map(Arc::from);
413 self
414 }
415
416 #[must_use]
417 pub fn is_empty(&self) -> bool {
418 self.stack.is_empty()
419 && self.memory.is_none()
420 && self.storage.is_empty()
421 && self.storage_diff.is_empty()
422 && self.transient_storage.is_empty()
423 && self.return_data.is_none()
424 }
425}
426
427impl Serialize for StepSnapshot {
428 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
429 let mut state = serializer.serialize_struct("StepSnapshot", 6)?;
430 state.serialize_field("stack", &self.stack)?;
431 state.serialize_field("memory", &self.memory.as_deref().map(Hex))?;
432 state.serialize_field("storage", &*self.storage)?;
433 state.serialize_field("storage_diff", &self.storage_diff)?;
434 if self.transient_storage.is_empty() {
436 state.skip_field("transient_storage")?;
437 } else {
438 state.serialize_field("transient_storage", &self.transient_storage)?;
439 }
440 if self.return_data.is_none() {
441 state.skip_field("return_data")?;
442 } else {
443 state.serialize_field("return_data", &self.return_data.as_deref())?;
444 }
445 state.end()
446 }
447}
448
449#[derive(Deserialize, Default)]
450struct StepSnapshotRepr {
451 #[serde(default)]
452 stack: Vec<Word>,
453 #[serde(default, deserialize_with = "deserialize_memory")]
454 memory: Option<Bytes>,
455 #[serde(default)]
456 storage: BTreeMap<String, String>,
457 #[serde(default)]
458 storage_diff: BTreeMap<String, StorageChange>,
459 #[serde(default)]
460 transient_storage: BTreeMap<String, String>,
461 #[serde(default)]
462 return_data: Option<String>,
463}
464
465impl From<StepSnapshotRepr> for StepSnapshot {
466 fn from(repr: StepSnapshotRepr) -> Self {
467 Self::new(repr.stack, repr.memory, repr.storage, repr.storage_diff)
468 .with_transient_storage(repr.transient_storage)
469 .with_return_data(repr.return_data)
470 }
471}
472
473#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
474pub struct StorageChange {
475 pub before: Option<String>,
476 pub after: Option<String>,
477}
478
479#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
485#[serde(from = "TransactionTraceRepr")]
486pub struct TransactionTrace {
487 pub tx_hash: Option<String>,
488 pub from_addr: String,
489 pub to_addr: Option<String>,
490 pub value: String,
491 pub input_data: String,
492 pub gas_used: u64,
493 pub output: String,
494 pub success: bool,
495 pub error: Option<String>,
496 pub debug_trace_available: bool,
497 pub contract_address: Option<String>,
498 #[serde(default)]
499 pub backend: Option<String>,
500 #[serde(default)]
501 pub capabilities: TraceCapabilities,
502 #[serde(default)]
503 pub artifacts: TraceArtifacts,
504 pub steps: StepTable,
505}
506
507#[derive(Deserialize)]
509struct TransactionTraceRepr {
510 tx_hash: Option<String>,
511 from_addr: String,
512 to_addr: Option<String>,
513 value: String,
514 input_data: String,
515 gas_used: u64,
516 output: String,
517 success: bool,
518 error: Option<String>,
519 debug_trace_available: bool,
520 contract_address: Option<String>,
521 #[serde(default)]
522 backend: Option<String>,
523 #[serde(default)]
524 capabilities: TraceCapabilities,
525 #[serde(default)]
526 artifacts: TraceArtifacts,
527 steps: StepTable,
528}
529
530impl From<TransactionTraceRepr> for TransactionTrace {
531 fn from(repr: TransactionTraceRepr) -> Self {
532 Self {
533 tx_hash: repr.tx_hash,
534 from_addr: repr.from_addr,
535 to_addr: repr.to_addr,
536 value: repr.value,
537 input_data: repr.input_data,
538 gas_used: repr.gas_used,
539 output: repr.output,
540 success: repr.success,
541 error: repr.error,
542 debug_trace_available: repr.debug_trace_available,
543 contract_address: repr.contract_address,
544 backend: repr.backend,
545 capabilities: repr.capabilities,
546 artifacts: repr.artifacts,
547 steps: repr.steps,
548 }
549 }
550}
551
552#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
553pub struct TraceCapabilities {
554 #[serde(default)]
555 pub opcode_steps: bool,
556 #[serde(default)]
557 pub stack: bool,
558 #[serde(default)]
559 pub memory: bool,
560 #[serde(default)]
561 pub storage: bool,
562 #[serde(default)]
563 pub storage_diff: bool,
564 #[serde(default)]
565 pub call_trace: bool,
566 #[serde(default)]
567 pub contract_creation: bool,
568 #[serde(default)]
569 pub logs: bool,
570 #[serde(default)]
571 pub revert_data: bool,
572 #[serde(default)]
573 pub gas_details: bool,
574 #[serde(default)]
575 pub account_changes: bool,
576 #[serde(default)]
578 pub transient_storage: bool,
579 #[serde(default)]
581 pub return_data: bool,
582 #[serde(default)]
583 pub notes: Vec<String>,
584}
585
586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
587pub struct TraceArtifacts {
588 #[serde(default)]
589 pub calls: Vec<ExecutionCall>,
590 #[serde(default)]
591 pub creations: Vec<ContractCreation>,
592 #[serde(default)]
593 pub logs: Vec<ExecutionLog>,
594 #[serde(default)]
595 pub account_changes: Vec<AccountChange>,
596 #[serde(default)]
597 pub gas: Option<GasSummary>,
598 #[serde(default)]
599 pub revert_data: Option<String>,
600 #[serde(default, skip_serializing_if = "Vec::is_empty")]
603 pub root_kinds: Vec<RootKind>,
604}
605
606#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
608pub struct RootKind {
609 pub entry_step: usize,
610 pub kind: String,
611}
612
613#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
614pub struct ExecutionCall {
615 pub id: usize,
616 pub parent_id: Option<usize>,
617 pub depth: u64,
618 #[serde(default, skip_serializing_if = "Option::is_none")]
619 pub entry_step: Option<usize>,
620 #[serde(default, skip_serializing_if = "Option::is_none")]
621 pub exit_step: Option<usize>,
622 pub call_type: String,
623 pub from: String,
624 pub to: String,
625 pub bytecode_address: String,
626 pub value: String,
627 pub input: String,
628 pub gas_limit: u64,
629 pub gas_used: Option<u64>,
630 pub output: Option<String>,
631 pub success: Option<bool>,
632 pub error: Option<String>,
633}
634
635#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
636pub struct ContractCreation {
637 pub id: usize,
638 pub parent_id: Option<usize>,
639 pub depth: u64,
640 #[serde(default, skip_serializing_if = "Option::is_none")]
641 pub entry_step: Option<usize>,
642 #[serde(default, skip_serializing_if = "Option::is_none")]
643 pub exit_step: Option<usize>,
644 pub create_type: String,
645 pub caller: String,
646 pub address: Option<String>,
647 pub value: String,
648 pub init_code: String,
649 pub gas_limit: u64,
650 pub gas_used: Option<u64>,
651 pub output: Option<String>,
652 pub success: Option<bool>,
653 pub error: Option<String>,
654}
655
656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
657pub struct ExecutionLog {
658 pub index: usize,
659 pub depth: u64,
660 pub address: String,
661 pub topics: Vec<String>,
662 pub data: String,
663}
664
665#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
666pub struct AccountChange {
667 pub depth: u64,
668 pub kind: String,
669 pub address: Option<String>,
670 pub from: Option<String>,
671 pub to: Option<String>,
672 pub value: Option<String>,
673 pub key: Option<String>,
674 pub previous_value: Option<String>,
675 pub previous_nonce: Option<u64>,
676}
677
678#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
679pub struct GasSummary {
680 pub used: u64,
681 pub spent: Option<u64>,
682 pub refunded: Option<u64>,
683 pub remaining: Option<u64>,
684 pub limit: Option<u64>,
685}
686
687#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
688pub struct FunctionCall {
689 pub name: String,
690 pub selector: String,
691 pub entry_step: usize,
692 pub exit_step: Option<usize>,
693 pub gas_used: u64,
694 pub depth: u64,
695 pub call_type: String,
696 pub contract_address: Option<String>,
697}
698
699#[must_use]
702pub fn same_buffer(a: &[u8], b: &[u8]) -> bool {
703 a.as_ptr() == b.as_ptr() && a.len() == b.len()
704}
705
706#[derive(Debug, Clone, PartialEq, Eq, Error)]
708pub enum HexError {
709 #[error("{0} hex digits is not a whole number of bytes")]
711 OddLength(usize),
712 #[error("`{character}` at offset {offset} is not a hex digit")]
714 InvalidDigit { offset: usize, character: char },
715}
716
717#[must_use]
720pub fn encode_hex(bytes: &[u8]) -> String {
721 let mut hex = String::with_capacity(bytes.len() * 2);
722 for byte in bytes {
723 hex.push(char::from(HEX_DIGITS[usize::from(byte >> 4)]));
724 hex.push(char::from(HEX_DIGITS[usize::from(byte & 0x0f)]));
725 }
726 hex
727}
728
729pub fn decode_hex(hex: &str) -> Result<Vec<u8>, HexError> {
736 let mut bytes = Vec::new();
737 decode_hex_into(hex, &mut bytes)?;
738 Ok(bytes)
739}
740
741pub fn decode_hex_into(hex: &str, bytes: &mut Vec<u8>) -> Result<(), HexError> {
747 let digits = hex
748 .strip_prefix("0x")
749 .or_else(|| hex.strip_prefix("0X"))
750 .unwrap_or(hex);
751 let raw = digits.as_bytes();
754 let invalid = |offset: usize| HexError::InvalidDigit {
755 offset,
756 character: digits
758 .get(offset..)
759 .and_then(|rest| rest.chars().next())
760 .unwrap_or(char::REPLACEMENT_CHARACTER),
761 };
762 bytes.reserve(raw.len() / 2);
763 let (pairs, rest) = raw.as_chunks::<2>();
764 for (index, [high, low]) in pairs.iter().enumerate() {
765 let high = hex_value(*high).ok_or_else(|| invalid(index * 2))?;
766 let low = hex_value(*low).ok_or_else(|| invalid(index * 2 + 1))?;
767 bytes.push(high << 4 | low);
768 }
769 if let [last] = rest {
770 return Err(match hex_value(*last) {
771 Some(_) => HexError::OddLength(raw.len()),
772 None => invalid(raw.len() - 1),
773 });
774 }
775 Ok(())
776}
777
778const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
779
780fn hex_value(digit: u8) -> Option<u8> {
781 match digit {
782 b'0'..=b'9' => Some(digit - b'0'),
783 b'a'..=b'f' => Some(digit - b'a' + 10),
784 b'A'..=b'F' => Some(digit - b'A' + 10),
785 _ => None,
786 }
787}
788
789#[derive(Clone, Copy)]
793pub(crate) struct Hex<'a>(pub(crate) &'a [u8]);
794
795impl fmt::Display for Hex<'_> {
796 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
797 let mut buffer = [0_u8; 128];
798 for chunk in self.0.chunks(buffer.len() / 2) {
799 for (index, byte) in chunk.iter().enumerate() {
800 buffer[index * 2] = HEX_DIGITS[usize::from(byte >> 4)];
801 buffer[index * 2 + 1] = HEX_DIGITS[usize::from(byte & 0x0f)];
802 }
803 let digits = std::str::from_utf8(&buffer[..chunk.len() * 2]).map_err(|_| fmt::Error)?;
804 formatter.write_str(digits)?;
805 }
806 Ok(())
807 }
808}
809
810impl Serialize for Hex<'_> {
811 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
812 serializer.collect_str(self)
813 }
814}
815
816fn deserialize_memory<'de, D: Deserializer<'de>>(
818 deserializer: D,
819) -> Result<Option<Bytes>, D::Error> {
820 struct MemoryHex;
821
822 impl<'de> Visitor<'de> for MemoryHex {
823 type Value = Option<Bytes>;
824
825 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
826 formatter.write_str("memory as a hex string")
827 }
828
829 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
830 Ok(None)
831 }
832
833 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
834 Ok(None)
835 }
836
837 fn visit_some<D: Deserializer<'de>>(
838 self,
839 deserializer: D,
840 ) -> Result<Self::Value, D::Error> {
841 deserializer.deserialize_str(self)
842 }
843
844 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
845 decode_hex(value)
846 .map(|bytes| Some(Bytes::from(bytes)))
847 .map_err(|error| E::custom(format!("memory is not hex: {error}")))
848 }
849 }
850
851 deserializer.deserialize_option(MemoryHex)
852}
853
854#[cfg(test)]
855mod tests {
856 #[test]
859 fn repeated_stack_words_are_stored_once() {
860 let mut words = super::WordInterner::new();
861 assert!(words.is_empty());
862 let first = words.intern("0x2a");
863 let again = words.intern("0x2a");
864 let other = words.intern("0x1");
865 assert_eq!(words.len(), 2);
866 assert_eq!(&*first, "0x2a");
867 assert!(Arc::ptr_eq(&first, &again));
871 assert!(!Arc::ptr_eq(&first, &other));
872
873 let mut stack = vec![Arc::from("0x2a"), Arc::from("0x2a"), Arc::from("0x1")];
874 words.intern_stack(&mut stack);
875 assert!(Arc::ptr_eq(&stack[0], &stack[1]));
876 assert!(Arc::ptr_eq(&stack[0], &first));
877 assert_eq!(words.len(), 2, "nothing new was seen");
878 }
879
880 #[test]
881 fn a_parsed_trace_reads_its_stack_words() {
882 let document = serde_json::json!({
884 "tx_hash": "0xabc",
885 "from_addr": "0x1",
886 "to_addr": "0x2",
887 "value": "0x0",
888 "input_data": "0x",
889 "gas_used": 0,
890 "output": "0x",
891 "success": true,
892 "debug_trace_available": true,
893 "steps": [
894 {"pc": 0, "op": "PUSH1", "gas": 1, "gas_cost": 1, "depth": 1,
895 "stack": ["0x2a", "0x1"]},
896 {"pc": 2, "op": "ADD", "gas": 0, "gas_cost": 1, "depth": 1,
897 "stack": ["0x002A", "0x1"]}
898 ]
899 })
900 .to_string();
901 let trace: TransactionTrace = serde_json::from_str(&document).expect("trace");
902 let stack = |index: usize| trace.steps.step(index).stack().to_vec();
903 assert_eq!(stack(0), [EvmWord::from(0x2a), EvmWord::from(1)]);
904 assert_eq!(stack(1), stack(0));
905 let written = serde_json::to_value(&trace).expect("json");
907 assert_eq!(
908 written["steps"][1]["stack"],
909 serde_json::json!(["0x2a", "0x1"])
910 );
911 }
912 fn words(stack: &[super::Word]) -> Vec<&str> {
913 stack.iter().map(|word| &**word).collect()
914 }
915
916 use std::collections::BTreeMap;
917 use std::sync::Arc;
918
919 use super::{
920 decode_hex, encode_hex, same_buffer, Bytes, EvmWord, FunctionCall, HexError, StepSnapshot,
921 StorageChange, TraceArtifacts, TraceCapabilities, TraceStep, TransactionTrace,
922 };
923
924 fn memory(hex: &str) -> Bytes {
926 Bytes::from(decode_hex(hex).expect("test memory is hex"))
927 }
928
929 fn step_with(snapshot: StepSnapshot) -> TraceStep {
930 TraceStep {
931 pc: 3,
932 op: Arc::from("SSTORE"),
933 gas: 100,
934 gas_cost: 5,
935 depth: 1,
936 stack: vec![Arc::from("0x01"), Arc::from("0x02")],
937 memory: Some(memory("aabb")),
938 storage: Some(BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())])),
939 error: None,
940 snapshot,
941 }
942 }
943
944 #[test]
945 fn borrowed_snapshot_matches_the_owned_one() {
946 let flat = step_with(StepSnapshot::default());
951 let populated = step_with(StepSnapshot::new(
952 vec![Arc::from("0x09")],
953 Some(memory("ccdd")),
954 BTreeMap::from([("0x01".to_owned(), "0x63".to_owned())]),
955 BTreeMap::from([(
956 "0x01".to_owned(),
957 StorageChange {
958 before: None,
959 after: Some("0x63".to_owned()),
960 },
961 )]),
962 ));
963
964 for step in [&flat, &populated] {
965 assert_eq!(
966 step.snapshot_ref().to_owned_snapshot(),
967 step.normalized_snapshot()
968 );
969 assert_eq!(
970 serde_json::to_string(&step.snapshot_ref()).expect("borrowed snapshot"),
971 serde_json::to_string(&step.normalized_snapshot()).expect("owned snapshot"),
972 );
973 }
974
975 assert_eq!(words(flat.snapshot_ref().stack), ["0x01", "0x02"]);
977 assert_eq!(flat.snapshot_ref().memory, Some(&[0xaa, 0xbb][..]));
978 assert!(flat.snapshot_ref().storage_diff.is_empty());
979
980 assert_eq!(words(populated.snapshot_ref().stack), ["0x09"]);
982 assert_eq!(populated.snapshot_ref().memory, Some(&[0xcc, 0xdd][..]));
983 assert_eq!(populated.snapshot_ref().storage_diff.len(), 1);
984 }
985
986 #[test]
987 fn steps_serialize_in_the_wire_format_and_read_back_stored_once() {
988 let step = TraceStep::new(
991 3,
992 Arc::from("SSTORE"),
993 100,
994 5,
995 1,
996 None,
997 StepSnapshot::new(
998 vec![Arc::from("0x01")],
999 Some(memory("aabb")),
1000 BTreeMap::from([("0x00".to_owned(), "0x2a".to_owned())]),
1001 BTreeMap::new(),
1002 ),
1003 );
1004 let json: serde_json::Value = serde_json::to_value(&step).expect("json");
1005 assert_eq!(json["stack"], serde_json::json!(["0x01"]));
1006 assert_eq!(json["memory"], "aabb");
1007 assert_eq!(json["storage"]["0x00"], "0x2a");
1008 assert_eq!(json["snapshot"]["memory"], "aabb");
1009 assert_eq!(json["snapshot"]["storage"]["0x00"], "0x2a");
1010 assert_eq!(json["gas_cost"], 5);
1011
1012 let flat = step_with(StepSnapshot::default());
1014 let flat_json: serde_json::Value = serde_json::to_value(&flat).expect("json");
1015 assert_eq!(
1016 flat_json["snapshot"]["stack"],
1017 serde_json::json!(["0x01", "0x02"])
1018 );
1019 assert_eq!(flat_json["stack"], serde_json::json!(["0x01", "0x02"]));
1020
1021 let restored: TraceStep = serde_json::from_value(json).expect("step");
1024 assert_eq!(restored, step);
1025 assert!(
1026 restored.stack.is_empty() && restored.memory.is_none() && restored.storage.is_none()
1027 );
1028 let legacy: TraceStep = serde_json::from_str(
1029 r#"{"pc":1,"op":"ADD","gas":9,"gas_cost":3,"depth":1,"stack":["0x1"],"memory":"cc","storage":{"0x0":"0x1"},"error":null}"#,
1030 )
1031 .expect("legacy step");
1032 assert_eq!(words(legacy.snapshot_ref().stack), ["0x1"]);
1033 assert_eq!(legacy.snapshot_ref().memory, Some(&[0xcc][..]));
1034 assert_eq!(legacy.snapshot_ref().storage["0x0"], "0x1");
1035 assert!(legacy.stack.is_empty());
1036 assert_eq!(legacy, step_with_legacy_fields());
1037
1038 let shared = TraceStep::new(
1040 4,
1041 Arc::from("POP"),
1042 95,
1043 2,
1044 1,
1045 None,
1046 StepSnapshot {
1047 stack: Vec::new(),
1048 memory: step.snapshot.memory.clone(),
1049 storage: Arc::clone(&step.snapshot.storage),
1050 storage_diff: BTreeMap::new(),
1051 transient_storage: BTreeMap::new(),
1052 return_data: None,
1053 },
1054 );
1055 assert!(Arc::ptr_eq(
1056 &shared.snapshot.storage,
1057 &step.snapshot.storage
1058 ));
1059 assert_eq!(shared.snapshot_ref().memory, Some(&[0xaa, 0xbb][..]));
1060 assert!(same_buffer(
1061 shared.snapshot_ref().memory.expect("memory"),
1062 step.snapshot_ref().memory.expect("memory")
1063 ));
1064 }
1065
1066 #[test]
1067 fn transient_storage_is_written_only_where_a_step_touched_it() {
1068 let plain = TraceStep::new(
1069 0,
1070 Arc::from("ADD"),
1071 1,
1072 1,
1073 1,
1074 None,
1075 StepSnapshot::new(
1076 vec![Arc::from("0x1")],
1077 None,
1078 BTreeMap::new(),
1079 BTreeMap::new(),
1080 ),
1081 );
1082 let json = serde_json::to_value(&plain).expect("json");
1083 assert!(json["snapshot"].get("transient_storage").is_none());
1084
1085 let touched = TraceStep::new(
1086 1,
1087 Arc::from("TSTORE"),
1088 1,
1089 100,
1090 1,
1091 None,
1092 StepSnapshot::new(
1093 vec![Arc::from("0x1")],
1094 None,
1095 BTreeMap::new(),
1096 BTreeMap::new(),
1097 )
1098 .with_transient_storage(BTreeMap::from([("0x0".to_owned(), "0x2".to_owned())])),
1099 );
1100 let json = serde_json::to_value(&touched).expect("json");
1101 assert_eq!(json["snapshot"]["transient_storage"]["0x0"], "0x2");
1102 assert_eq!(
1103 serde_json::to_value(touched.snapshot_ref()).expect("borrowed"),
1104 json["snapshot"]
1105 );
1106 let restored: TraceStep = serde_json::from_value(json).expect("step");
1107 assert_eq!(restored, touched);
1108 assert_eq!(restored.snapshot_ref().transient_storage["0x0"], "0x2");
1109
1110 let capabilities: TraceCapabilities =
1113 serde_json::from_str(r#"{"stack":true}"#).expect("capabilities");
1114 assert!(!capabilities.transient_storage);
1115 }
1116
1117 #[test]
1118 fn return_data_is_written_only_where_the_buffer_holds_some() {
1119 let snapshot = || {
1120 StepSnapshot::new(
1121 vec![Arc::from("0x1")],
1122 None,
1123 BTreeMap::new(),
1124 BTreeMap::new(),
1125 )
1126 };
1127 let empty = TraceStep::new(
1128 0,
1129 Arc::from("CALL"),
1130 1,
1131 1,
1132 1,
1133 None,
1134 snapshot().with_return_data(Some(String::new())),
1135 );
1136 assert!(empty.snapshot.return_data.is_none());
1137 let json = serde_json::to_value(&empty).expect("json");
1138 assert!(json["snapshot"].get("return_data").is_none());
1139
1140 let filled = TraceStep::new(
1141 1,
1142 Arc::from("RETURNDATASIZE"),
1143 1,
1144 2,
1145 1,
1146 None,
1147 snapshot().with_return_data(Some("2a".to_owned())),
1148 );
1149 let json = serde_json::to_value(&filled).expect("json");
1150 assert_eq!(json["snapshot"]["return_data"], "2a");
1151 assert_eq!(
1152 serde_json::to_value(filled.snapshot_ref()).expect("borrowed"),
1153 json["snapshot"]
1154 );
1155 let restored: TraceStep = serde_json::from_value(json).expect("step");
1156 assert_eq!(restored, filled);
1157 assert_eq!(restored.snapshot_ref().return_data, Some("2a"));
1158
1159 let mut trace = TransactionTrace {
1161 tx_hash: None,
1162 from_addr: "0x1".to_owned(),
1163 to_addr: None,
1164 value: "0x0".to_owned(),
1165 input_data: "0x".to_owned(),
1166 gas_used: 0,
1167 output: "0x".to_owned(),
1168 success: true,
1169 error: None,
1170 debug_trace_available: true,
1171 contract_address: None,
1172 backend: None,
1173 capabilities: TraceCapabilities::default(),
1174 artifacts: TraceArtifacts::default(),
1175 steps: vec![filled.clone(), filled].into(),
1176 };
1177 trace = serde_json::from_str(&serde_json::to_string(&trace).expect("json")).expect("trace");
1178 let data = |index: usize| trace.steps.step(index).return_data().expect("data");
1179 assert!(std::ptr::eq(data(0), data(1)));
1180 assert!(!trace.capabilities.return_data);
1181 }
1182
1183 fn step_with_legacy_fields() -> TraceStep {
1184 TraceStep {
1185 pc: 1,
1186 op: Arc::from("ADD"),
1187 gas: 9,
1188 gas_cost: 3,
1189 depth: 1,
1190 stack: vec![Arc::from("0x1")],
1191 memory: Some(memory("cc")),
1192 storage: Some(BTreeMap::from([("0x0".to_owned(), "0x1".to_owned())])),
1193 error: None,
1194 snapshot: StepSnapshot::default(),
1195 }
1196 }
1197
1198 #[test]
1199 fn reading_a_trace_back_shares_unchanged_state_between_steps() {
1200 let snapshot = |hex: &str, slot: &str| {
1201 StepSnapshot::new(
1202 Vec::new(),
1203 Some(memory(hex)),
1204 BTreeMap::from([("0x0".to_owned(), slot.to_owned())]),
1205 BTreeMap::new(),
1206 )
1207 };
1208 let step = |pc: u64, snapshot: StepSnapshot| {
1209 TraceStep::new(pc, Arc::from("PUSH1"), 1, 1, 1, None, snapshot)
1210 };
1211 let trace = TransactionTrace {
1212 tx_hash: None,
1213 from_addr: "0x1".to_owned(),
1214 to_addr: None,
1215 value: "0x0".to_owned(),
1216 input_data: "0x".to_owned(),
1217 gas_used: 0,
1218 output: "0x".to_owned(),
1219 success: true,
1220 error: None,
1221 debug_trace_available: true,
1222 contract_address: None,
1223 backend: None,
1224 capabilities: TraceCapabilities::default(),
1225 artifacts: TraceArtifacts::default(),
1226 steps: vec![
1227 step(0, snapshot("aa", "0x1")),
1228 step(1, snapshot("aa", "0x1")),
1229 step(2, snapshot("bb", "0x1")),
1230 step(3, snapshot("bb", "0x2")),
1231 ]
1232 .into(),
1233 };
1234 let json = serde_json::to_string(&trace).expect("json");
1235 let restored: TransactionTrace = serde_json::from_str(&json).expect("trace");
1236 assert_eq!(restored, trace);
1237 for trace in [&restored, &trace] {
1240 let memory = |index: usize| trace.steps.step(index).memory().expect("memory");
1241 assert!(same_buffer(&memory(0), &memory(1)));
1242 assert!(!same_buffer(&memory(1), &memory(2)));
1243 assert!(same_buffer(&memory(2), &memory(3)));
1244 let storage = |index: usize| trace.steps.step(index).storage();
1245 assert!(std::ptr::eq(storage(0), storage(2)));
1246 assert!(!std::ptr::eq(storage(2), storage(3)));
1247 }
1248 }
1249
1250 #[test]
1251 fn core_models_are_serializable() {
1252 let trace = TransactionTrace {
1253 tx_hash: Some("0xabc".to_owned()),
1254 from_addr: "0x1".to_owned(),
1255 to_addr: Some("0x2".to_owned()),
1256 value: "0".to_owned(),
1257 input_data: "0x".to_owned(),
1258 gas_used: 1,
1259 output: "0x".to_owned(),
1260 success: true,
1261 error: None,
1262 steps: vec![TraceStep {
1263 pc: 0,
1264 op: Arc::from("STOP"),
1265 gas: 1,
1266 gas_cost: 0,
1267 depth: 0,
1268 stack: Vec::new(),
1269 memory: None,
1270 storage: None,
1271 error: None,
1272 snapshot: StepSnapshot::default(),
1273 }]
1274 .into(),
1275 debug_trace_available: true,
1276 contract_address: None,
1277 backend: Some("debug-rpc".to_owned()),
1278 capabilities: TraceCapabilities::default(),
1279 artifacts: TraceArtifacts::default(),
1280 };
1281
1282 let encoded = serde_json::to_string(&trace).expect("trace serializes");
1283 assert!(encoded.contains("\"success\":true"));
1284
1285 let call = FunctionCall {
1286 name: "runtime_dispatcher".to_owned(),
1287 selector: String::new(),
1288 entry_step: 0,
1289 exit_step: Some(0),
1290 gas_used: 1,
1291 depth: 0,
1292 call_type: "entry".to_owned(),
1293 contract_address: Some("0x2".to_owned()),
1294 };
1295 assert_eq!(call.call_type, "entry");
1296
1297 let change = StorageChange {
1298 before: None,
1299 after: Some("0x2a".to_owned()),
1300 };
1301 assert_eq!(change.after.as_deref(), Some("0x2a"));
1302 }
1303
1304 #[test]
1305 fn memory_reads_and_writes_as_the_same_hex_string() {
1306 let words = format!("{}{}", "00".repeat(31), "2a").repeat(3);
1310 let document = serde_json::json!({
1311 "tx_hash": "0xabc",
1312 "from_addr": "0x1",
1313 "to_addr": "0x2",
1314 "value": "0x0",
1315 "input_data": "0x",
1316 "gas_used": 0,
1317 "output": "0x",
1318 "success": true,
1319 "error": null,
1320 "debug_trace_available": true,
1321 "contract_address": null,
1322 "backend": "replay",
1323 "capabilities": TraceCapabilities::default(),
1324 "artifacts": TraceArtifacts::default(),
1325 "steps": [
1326 {"pc": 0, "op": "MSTORE", "gas": 1, "gas_cost": 1, "depth": 1,
1327 "stack": [], "memory": "", "storage": {}, "error": null,
1328 "snapshot": {"stack": [], "memory": "", "storage": {}, "storage_diff": {}}},
1329 {"pc": 1, "op": "MLOAD", "gas": 1, "gas_cost": 1, "depth": 1,
1330 "stack": [], "memory": words, "storage": {}, "error": null,
1331 "snapshot": {"stack": [], "memory": words, "storage": {}, "storage_diff": {}}},
1332 {"pc": 2, "op": "POP", "gas": 1, "gas_cost": 1, "depth": 1,
1333 "stack": [], "memory": words, "storage": {}, "error": null,
1334 "snapshot": {"stack": [], "memory": words, "storage": {}, "storage_diff": {}}},
1335 {"pc": 3, "op": "STOP", "gas": 1, "gas_cost": 0, "depth": 1,
1336 "stack": [], "memory": null, "storage": {}, "error": null,
1337 "snapshot": {"stack": [], "memory": null, "storage": {}, "storage_diff": {}}}
1338 ]
1339 })
1340 .to_string();
1341 let trace: TransactionTrace = serde_json::from_str(&document).expect("trace");
1342 let written = serde_json::to_string(&trace).expect("json");
1343 assert_eq!(
1344 serde_json::from_str::<serde_json::Value>(&written).expect("written json"),
1345 serde_json::from_str::<serde_json::Value>(&document).expect("document json"),
1346 );
1347 let again: TransactionTrace = serde_json::from_str(&written).expect("trace");
1349 assert_eq!(serde_json::to_string(&again).expect("json"), written);
1350
1351 let memory = |index: usize| trace.steps.step(index).memory();
1352 assert_eq!(memory(0).as_deref(), Some(&[][..]));
1353 assert_eq!(memory(1).map(|memory| memory.len()), Some(96));
1354 assert_eq!(
1355 memory(1).and_then(|memory| memory.get(31).copied()),
1356 Some(0x2a)
1357 );
1358 assert_eq!(memory(3), None);
1359 assert!(same_buffer(
1361 &memory(1).expect("memory"),
1362 &memory(2).expect("memory")
1363 ));
1364 }
1365
1366 #[test]
1367 fn memory_from_a_file_may_be_prefixed_but_must_be_hex() {
1368 let step = |memory: &str| {
1369 serde_json::from_str::<TraceStep>(&format!(
1370 r#"{{"pc":0,"op":"STOP","gas":0,"gas_cost":0,"depth":1,"memory":{memory}}}"#
1371 ))
1372 };
1373 let prefixed = step(r#""0xAABB""#).expect("prefixed memory");
1374 assert_eq!(prefixed.snapshot_ref().memory, Some(&[0xaa, 0xbb][..]));
1375 assert_eq!(
1377 serde_json::to_value(&prefixed).expect("json")["memory"],
1378 "aabb"
1379 );
1380
1381 let odd = step(r#""abc""#).expect_err("odd length");
1382 assert!(odd
1383 .to_string()
1384 .starts_with("memory is not hex: 3 hex digits"));
1385 let invalid = step(r#""aazz""#).expect_err("not hex");
1386 assert!(invalid
1387 .to_string()
1388 .starts_with("memory is not hex: `z` at offset 2"));
1389 }
1390
1391 #[test]
1392 fn hex_round_trips_and_names_what_is_wrong() {
1393 assert_eq!(encode_hex(&[0x00, 0x2a, 0xff]), "002aff");
1394 assert_eq!(decode_hex("002AfF"), Ok(vec![0x00, 0x2a, 0xff]));
1395 assert_eq!(decode_hex("0x"), Ok(Vec::new()));
1396 assert_eq!(decode_hex(""), Ok(Vec::new()));
1397 assert_eq!(decode_hex("0x2"), Err(HexError::OddLength(1)));
1398 assert_eq!(
1399 decode_hex("2x"),
1400 Err(HexError::InvalidDigit {
1401 offset: 1,
1402 character: 'x'
1403 })
1404 );
1405 assert_eq!(
1407 decode_hex("aé"),
1408 Err(HexError::InvalidDigit {
1409 offset: 1,
1410 character: 'é'
1411 })
1412 );
1413 }
1414}