alloy_rpc_types_debug/debug.rs
1//! Types for the `debug` API.
2
3use alloc::{collections::btree_map::BTreeMap, vec::Vec};
4use alloy_primitives::{Bytes, StorageKey, B256};
5use derive_more::{AsMut, AsRef, Deref, DerefMut};
6use serde::{Deserialize, Serialize};
7
8/// Represents the result of a storage slot query.
9#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct StorageResult {
12 /// The storage key
13 pub key: StorageKey,
14 /// The value stored at the slot
15 pub value: B256,
16}
17
18impl From<(StorageKey, B256)> for StorageResult {
19 fn from((key, value): (StorageKey, B256)) -> Self {
20 Self { key, value }
21 }
22}
23
24impl From<StorageResult> for (StorageKey, B256) {
25 fn from(result: StorageResult) -> Self {
26 (result.key, result.value)
27 }
28}
29
30/// Wrapper type for a map of storage slots.
31#[derive(
32 Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, AsRef, Deref, AsMut, DerefMut,
33)]
34#[serde(rename_all = "camelCase")]
35pub struct StorageMap(pub BTreeMap<B256, StorageResult>);
36
37impl From<BTreeMap<B256, StorageResult>> for StorageMap {
38 fn from(map: BTreeMap<B256, StorageResult>) -> Self {
39 Self(map)
40 }
41}
42
43impl From<StorageMap> for BTreeMap<B256, StorageResult> {
44 fn from(map: StorageMap) -> Self {
45 map.0
46 }
47}
48
49/// Represents the result of a storage range query.
50#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct StorageRangeResult {
53 /// A map of storage slots
54 pub storage: StorageMap,
55 /// The next key
56 pub next_key: Option<B256>,
57}
58
59impl From<(StorageMap, Option<B256>)> for StorageRangeResult {
60 fn from((storage, next_key): (StorageMap, Option<B256>)) -> Self {
61 Self { storage, next_key }
62 }
63}
64
65impl From<StorageRangeResult> for (StorageMap, Option<B256>) {
66 fn from(result: StorageRangeResult) -> Self {
67 (result.storage, result.next_key)
68 }
69}
70
71/// Represents the execution witness of a block. Contains an optional map of state preimages.
72#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
73pub struct ExecutionWitness {
74 /// List of all hashed trie nodes preimages that were required during the execution of
75 /// the block, including during state root recomputation.
76 pub state: Vec<Bytes>,
77 /// List of all contract codes (created / accessed) preimages that were required during
78 /// the execution of the block, including during state root recomputation.
79 pub codes: Vec<Bytes>,
80 /// List of all hashed account and storage keys (addresses and slots) preimages
81 /// (unhashed account addresses and storage slots, respectively) that were required during
82 /// the execution of the block.
83 pub keys: Vec<Bytes>,
84 /// Block headers required for proving correctness of stateless execution.
85 ///
86 /// This collection stores ancestor(parent) block headers needed to verify:
87 /// - State reads are correct (ie the code and accounts are correct wrt the pre-state root)
88 /// - BLOCKHASH opcode execution results are correct
89 ///
90 /// ## Why this field will be empty in the future
91 ///
92 /// This field is expected to be empty in the future because:
93 /// - EIP-2935 (Prague) will include block hashes directly in the state
94 /// - Verkle/Delayed execution will change the block structure to contain the pre-state root
95 /// instead of the post-state root.
96 ///
97 /// Once both of these upgrades have been implemented, this field will be empty
98 /// moving forward because the data that this was proving will either be in the
99 /// current block or in the state.
100 ///
101 /// ## State Read Verification
102 ///
103 /// To verify state reads are correct, we need the pre-state root of the current block,
104 /// which is (currently) equal to the post-state root of the previous block. We therefore
105 /// need the previous block's header in order to prove that the state reads are correct.
106 ///
107 /// Note: While the pre-state root is located in the previous block, this field
108 /// will always have one or more items.
109 ///
110 /// ## BLOCKHASH Opcode Verification
111 ///
112 /// The BLOCKHASH opcode returns the block hash for a given block number, but it
113 /// only works for the 256 most recent blocks, not including the current block.
114 /// To verify that a block hash is indeed correct wrt the BLOCKHASH opcode
115 /// and not an arbitrary set of block hashes, we need a contiguous set of
116 /// block headers starting from the current block.
117 ///
118 /// ### Example
119 ///
120 /// Consider a blockchain at block 200, and inside of block 200, a transaction
121 /// calls BLOCKHASH(100):
122 /// - This is valid because block 100 is within the 256-block lookback window
123 /// - To verify this, we need all of the headers from block 100 through block 200
124 /// - These headers form a chain proving the correctness of block 100's hash.
125 ///
126 /// The naive way to construct the headers would be to unconditionally include the last
127 /// 256 block headers. However note, we may not need all 256, like in the example above.
128 pub headers: Vec<Bytes>,
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use serde_json::json;
135
136 #[test]
137 fn test_storage_range_result_roundtrip() {
138 let json_input = json!({
139 "storage": {
140 "0x0000000000000000000000000000000000000000000000000000000000000002": {
141 "key": "0x0000000000000000000000000000000000000000000000000000000000000002",
142 "value": "0x000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
143 },
144 "0x0000000000000000000000000000000000000000000000000000000000000003": {
145 "key": "0x0000000000000000000000000000000000000000000000000000000000000003",
146 "value": "0x0000000000000000000000000000000000000000000000000000000000000006"
147 }
148 },
149 "nextKey": "0x0000000000000000000000000000000000000000000000000000000000000004"
150 });
151
152 let parsed: StorageRangeResult = serde_json::from_value(json_input.clone()).unwrap();
153
154 let output = serde_json::to_value(&parsed).unwrap();
155
156 assert_eq!(json_input, output);
157 }
158}