cfx_rpc_eth_types/
state_dump.rs

1use cfx_rpc_primitives::Bytes;
2use cfx_types::{Address, StorageKey, StorageValue, H256, U256};
3use serde::{Deserialize, Serialize};
4use serde_with::{base64::Base64, serde_as};
5use std::collections::BTreeMap;
6
7// Empty storage trie root
8// 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
9pub const EOA_STORAGE_ROOT_H256: H256 = H256([
10    0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6,
11    0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0,
12    0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21,
13]);
14
15/// Represents the state of an account in the Ethereum state trie.
16#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
17#[serde(rename_all = "camelCase")]
18pub struct AccountState {
19    /// The balance of the account
20    pub balance: U256,
21    /// The nonce of the account
22    pub nonce: u64,
23    /// The root hash of the account
24    pub root: H256,
25    /// The code hash of the account
26    pub code_hash: H256,
27    /// The code of the account
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub code: Option<Bytes>,
30    /// A map of storage slots, indexed by storage key
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub storage: Option<BTreeMap<StorageKey, StorageValue>>,
33    /// Address only present in iterative (line-by-line) mode
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub address: Option<Address>,
36    /// If we don't have address, we can output the key
37    #[serde(skip_serializing_if = "Option::is_none")]
38    #[serde(rename = "key")]
39    pub address_hash: Option<H256>,
40}
41
42/// Represents a state dump, which includes the root hash of the state trie,
43/// Note: There are some differences in JSON serialization compared to geth's
44/// output, such as:
45/// - The root field in geth doesn't have a 0x prefix, while here it does
46/// - The balance field of accounts in geth is a decimal string, while here it's
47///   a hexadecimal string
48/// - The value field of storage in geth doesn't have a 0x prefix, while here it
49///   does
50#[serde_as]
51#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct StateDump {
54    /// The root hash of the state trie
55    pub root: H256,
56    /// A map of accounts, indexed by address
57    pub accounts: BTreeMap<Address, AccountState>,
58    /// Next can be set to represent that this dump is only partial, and Next
59    /// is where an iterator should be positioned in order to continue the
60    /// dump.
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[serde_as(as = "Option<Base64>")]
63    pub next: Option<Bytes>,
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use serde_json::json;
70
71    #[test]
72    fn test_state_dump_serialization() {
73        let json_input = json!({
74            "root": "0x5a1f70040e967bef6a32ee65e7fa2c3ea580e277e42cf3e3daf60a677ef18127",
75            "accounts": {
76                "0x000baa01f2a21d29dce20b88032752b990dac124": {
77                    "balance": "0x10000000000000000000",
78                    "nonce": 0,
79                    "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
80                    "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
81                    "address": "0x000baa01f2a21d29dce20b88032752b990dac124",
82                    "key": "0x000108a52c8b050f1098144f89e0b8e7e41310ea139f020b690b56e424508f4c"
83                },
84                "0x201d43c399f2495e19a591eab93fa3384ec6c72e": {
85                    "balance": "0x0",
86                    "nonce": 1,
87                    "root": "0x297c068574a50ffef03843dda4075c3b6b5790be78b30e3c9df4e02e4ba9125c",
88                    "codeHash": "0xbe6e2f7cdf118a0b2092927e0a0cf4a54316165ac5172bcda327939e04c9818f",
89                    "code": "0x36602c57343d527f9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff593da1005b363d3d373d3d3d3d610076806062363936013d732efa42b7d7591cbf436cce4973f900d8314c86dd5af43d3d93803e606057fd5bf34ad30ecfb92b9311a853d296c515fb0d6505d89c68db32372fd77e57b0879f97224bb89dac59e267486b38ee20309c8cc1acfb854eb9303a31c50a42f48a8fcc63b84d60abf8c5408ea569569af66c0cc3a76f6e00000000000000000000000000000000000000000000000000000000000af9ac0076",
90                    "storage": {
91                        "0x0000000000000000000000000000000000000000000000000000000000000000": "0x100000000000000000000000000686f559c",
92                        "0x0000000000000000000000000000000000000000000000000000000000000002": "0x1",
93                        "0x0000000000000000000000000000000000000000000000000000000000000008": "0xdead000000000000000000000000000000000000000000000000000000000000",
94                        "0x000000000000000000000000000000000000000000000000000000000000000a": "0x1",
95                        "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace": "0xffffffff",
96                        "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5acf": "0x4ad30ecfb92b9311a853d296c515fb0d6505d89c",
97                        "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad1": "0x68db32372fd77e57b0879f97224bb89dac59e267486b38ee20309c8cc1acfb85",
98                        "0x405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ad2": "0x686f559c00000000000000000000000000000001"
99                    },
100                    "address": "0x201d43c399f2495e19a591eab93fa3384ec6c72e",
101                    "key": "0x0000e65fdfaa2681656a211a55bc6fdcfe918f34cc037407ba12874c16cd7da9"
102                }
103            },
104            "next": "AAEx7TCXUlkysLMMJcS/W974Ue7bbhgSK3EUHVNFCtQ="
105        });
106
107        let parsed: StateDump =
108            serde_json::from_value(json_input.clone()).unwrap();
109        let output = serde_json::to_value(&parsed).unwrap();
110        assert_eq!(json_input, output);
111    }
112}