1use crate::account::{AccessKey, Account};
2use crate::hash::{hash, CryptoHash};
3use crate::receipt::{Receipt, ReceivedData};
4use crate::trie_key::trie_key_parsers::{
5 parse_account_id_from_access_key_key, parse_account_id_from_account_key,
6 parse_account_id_from_contract_code_key, parse_account_id_from_contract_data_key,
7 parse_account_id_from_received_data_key, parse_data_id_from_received_data_key,
8 parse_data_key_from_contract_data_key, parse_public_key_from_access_key_key,
9};
10use crate::trie_key::{col, TrieKey};
11use crate::types::{AccountId, StoreKey, StoreValue};
12use borsh::BorshDeserialize;
13use serde_with::base64::Base64;
14use serde_with::serde_as;
15use std::fmt::{Display, Formatter};
16use unc_crypto::PublicKey;
17
18#[serde_as]
20#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
21pub enum StateRecord {
22 Account { account_id: AccountId, account: Account },
24 Data { account_id: AccountId, data_key: StoreKey, value: StoreValue },
26 Contract {
28 account_id: AccountId,
29 #[serde_as(as = "Base64")]
30 code: Vec<u8>,
31 },
32 AccessKey { account_id: AccountId, public_key: PublicKey, access_key: AccessKey },
34 PostponedReceipt(Box<Receipt>),
36 ReceivedData {
38 account_id: AccountId,
39 data_id: CryptoHash,
40 #[serde_as(as = "Option<Base64>")]
41 data: Option<Vec<u8>>,
42 },
43 DelayedReceipt(Box<Receipt>),
46}
47
48impl StateRecord {
49 pub fn from_raw_key_value(key: Vec<u8>, value: Vec<u8>) -> Option<StateRecord> {
54 Self::from_raw_key_value_impl(key, value).unwrap_or(None)
55 }
56
57 pub fn from_raw_key_value_impl(
58 key: Vec<u8>,
59 value: Vec<u8>,
60 ) -> Result<Option<StateRecord>, std::io::Error> {
61 Ok(match key[0] {
62 col::ACCOUNT => Some(StateRecord::Account {
63 account_id: parse_account_id_from_account_key(&key)?,
64 account: Account::try_from_slice(&value)?,
65 }),
66 col::CONTRACT_DATA => {
67 let account_id = parse_account_id_from_contract_data_key(&key)?;
68 let data_key = parse_data_key_from_contract_data_key(&key, &account_id)?;
69 Some(StateRecord::Data {
70 account_id,
71 data_key: data_key.to_vec().into(),
72 value: value.into(),
73 })
74 }
75 col::CONTRACT_CODE => Some(StateRecord::Contract {
76 account_id: parse_account_id_from_contract_code_key(&key)?,
77 code: value,
78 }),
79 col::ACCESS_KEY => {
80 let access_key = AccessKey::try_from_slice(&value)?;
81 let account_id = parse_account_id_from_access_key_key(&key)?;
82 let public_key = parse_public_key_from_access_key_key(&key, &account_id)?;
83 Some(StateRecord::AccessKey { account_id, public_key, access_key })
84 }
85 col::RECEIVED_DATA => {
86 let data = ReceivedData::try_from_slice(&value)?.data;
87 let account_id = parse_account_id_from_received_data_key(&key)?;
88 let data_id = parse_data_id_from_received_data_key(&key, &account_id)?;
89 Some(StateRecord::ReceivedData { account_id, data_id, data })
90 }
91 col::POSTPONED_RECEIPT_ID => None,
92 col::PENDING_DATA_COUNT => None,
93 col::POSTPONED_RECEIPT => {
94 let receipt = Receipt::try_from_slice(&value)?;
95 Some(StateRecord::PostponedReceipt(Box::new(receipt)))
96 }
97 col::DELAYED_RECEIPT_OR_INDICES
98 if key.len() == TrieKey::DelayedReceiptIndices.len() =>
99 {
100 None
101 }
102 col::DELAYED_RECEIPT_OR_INDICES => {
103 let receipt = Receipt::try_from_slice(&value)?;
104 Some(StateRecord::DelayedReceipt(Box::new(receipt)))
105 }
106 _ => {
107 println!("key[0]: {} is unreachable", key[0]);
108 None
109 }
110 })
111 }
112
113 pub fn get_type_string(&self) -> String {
114 match self {
115 StateRecord::Account { .. } => "Account",
116 StateRecord::Data { .. } => "Data",
117 StateRecord::Contract { .. } => "Contract",
118 StateRecord::AccessKey { .. } => "AccessKey",
119 StateRecord::PostponedReceipt { .. } => "PostponedReceipt",
120 StateRecord::ReceivedData { .. } => "ReceivedData",
121 StateRecord::DelayedReceipt { .. } => "DelayedReceipt",
122 }
123 .to_string()
124 }
125}
126
127impl Display for StateRecord {
128 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
129 match self {
130 StateRecord::Account { account_id, account } => {
131 write!(f, "Account {:?}: {:?}", account_id, account)
132 }
133 StateRecord::Data { account_id, data_key, value } => write!(
134 f,
135 "Storage {:?},{:?}: {:?}",
136 account_id,
137 to_printable(data_key.as_ref()),
138 to_printable(value.as_ref())
139 ),
140 StateRecord::Contract { account_id, code: _ } => {
141 write!(f, "Code for {:?}: ...", account_id)
142 }
143 StateRecord::AccessKey { account_id, public_key, access_key } => {
144 write!(f, "Access key {:?},{:?}: {:?}", account_id, public_key, access_key)
145 }
146 StateRecord::ReceivedData { account_id, data_id, data } => write!(
147 f,
148 "Received data {:?},{:?}: {:?}",
149 account_id,
150 data_id,
151 data.as_ref().map(|v| to_printable(v))
152 ),
153 StateRecord::PostponedReceipt(receipt) => write!(f, "Postponed receipt {:?}", receipt),
154 StateRecord::DelayedReceipt(receipt) => write!(f, "Delayed receipt {:?}", receipt),
155 }
156 }
157}
158
159fn to_printable(blob: &[u8]) -> String {
160 if blob.len() > 60 {
161 format!("{} bytes, hash: {}", blob.len(), hash(blob))
162 } else {
163 let ugly = blob.iter().any(|&x| x < b' ');
164 if ugly {
165 return format!("0x{}", hex::encode(blob));
166 }
167 match String::from_utf8(blob.to_vec()) {
168 Ok(v) => v,
169 Err(_e) => format!("0x{}", hex::encode(blob)),
170 }
171 }
172}
173
174pub fn state_record_to_account_id(state_record: &StateRecord) -> &AccountId {
175 match state_record {
176 StateRecord::Account { account_id, .. }
177 | StateRecord::AccessKey { account_id, .. }
178 | StateRecord::Contract { account_id, .. }
179 | StateRecord::ReceivedData { account_id, .. }
180 | StateRecord::Data { account_id, .. } => account_id,
181 StateRecord::PostponedReceipt(receipt) | StateRecord::DelayedReceipt(receipt) => {
182 &receipt.receiver_id
183 }
184 }
185}
186
187pub fn is_contract_code_key(key: &[u8]) -> bool {
188 debug_assert!(!key.is_empty());
189 key[0] == col::CONTRACT_CODE
190}