unc_primitives/
trie_key.rs

1use std::mem::size_of;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4
5use unc_crypto::PublicKey;
6
7use crate::hash::CryptoHash;
8use crate::types::AccountId;
9
10pub(crate) const ACCOUNT_DATA_SEPARATOR: u8 = b',';
11// The use of `ACCESS_KEY` as a separator is a historical artefact.
12// Changing it would require a very long DB migration for basically no benefits.
13pub(crate) const ACCESS_KEY_SEPARATOR: u8 = col::ACCESS_KEY;
14
15pub(crate) const RSA2048_KEY_SEPARATOR: u8 = col::RSA2048_KEY;
16
17/// Type identifiers used for DB key generation to store values in the key-value storage.
18pub mod col {
19    /// This column id is used when storing `primitives::account::Account` type about a given
20    /// `account_id`.
21    pub const ACCOUNT: u8 = 0;
22    /// This column id is used when storing contract blob for a given `account_id`.
23    pub const CONTRACT_CODE: u8 = 1;
24    /// This column id is used when storing `primitives::account::AccessKey` type for a given
25    /// `account_id`.
26    pub const ACCESS_KEY: u8 = 2;
27    /// This column id is used when storing `primitives::receipt::ReceivedData` type (data received
28    /// for a key `data_id`). The required postponed receipt might be still not received or requires
29    /// more pending input data.
30    pub const RECEIVED_DATA: u8 = 3;
31    /// This column id is used when storing `primitives::hash::CryptoHash` (ReceiptId) type. The
32    /// ReceivedData is not available and is needed for the postponed receipt to execute.
33    pub const POSTPONED_RECEIPT_ID: u8 = 4;
34    /// This column id is used when storing the number of missing data inputs that are still not
35    /// available for a key `receipt_id`.
36    pub const PENDING_DATA_COUNT: u8 = 5;
37    /// This column id is used when storing the postponed receipts (`primitives::receipt::Receipt`).
38    pub const POSTPONED_RECEIPT: u8 = 6;
39    /// This column id is used when storing:
40    /// * the indices of the delayed receipts queue (a singleton per shard)
41    /// * the delayed receipts themselves
42    /// The identifier is shared between two different key types for for historical reasons. It
43    /// is valid because the length of `TrieKey::DelayedReceipt` is always greater than
44    /// `TrieKey::DelayedReceiptIndices` when serialized to bytes.
45    pub const DELAYED_RECEIPT_OR_INDICES: u8 = 7;
46    /// This column id is used when storing Key-Value data from a contract on an `account_id`.
47    pub const CONTRACT_DATA: u8 = 9;
48
49    pub const RSA2048_KEY: u8 = 10;
50    /// All columns
51    pub const NON_DELAYED_RECEIPT_COLUMNS: [(u8, &str); 8] = [
52        (ACCOUNT, "Account"),
53        (CONTRACT_CODE, "ContractCode"),
54        (ACCESS_KEY, "AccessKey"),
55        (RECEIVED_DATA, "ReceivedData"),
56        (POSTPONED_RECEIPT_ID, "PostponedReceiptId"),
57        (PENDING_DATA_COUNT, "PendingDataCount"),
58        (POSTPONED_RECEIPT, "PostponedReceipt"),
59        (CONTRACT_DATA, "ContractData"),
60    ];
61}
62
63/// Describes the key of a specific key-value record in a state trie.
64#[derive(Debug, Clone, PartialEq, Eq, BorshDeserialize, BorshSerialize)]
65pub enum TrieKey {
66    /// Used to store `primitives::account::Account` struct for a given `AccountId`.
67    Account { account_id: AccountId },
68    /// Used to store `Vec<u8>` contract code for a given `AccountId`.
69    ContractCode { account_id: AccountId },
70    /// Used to store `primitives::account::AccessKey` struct for a given `AccountId` and
71    /// a given `public_key` of the `AccessKey`.
72    AccessKey { account_id: AccountId, public_key: PublicKey },
73    /// Used to store `primitives::receipt::ReceivedData` struct for a given receiver's `AccountId`
74    /// of `DataReceipt` and a given `data_id` (the unique identifier for the data).
75    /// NOTE: This is one of the input data for some action receipt.
76    /// The action receipt might be still not be received or requires more pending input data.
77    ReceivedData { receiver_id: AccountId, data_id: CryptoHash },
78    /// Used to store receipt ID `primitives::hash::CryptoHash` for a given receiver's `AccountId`
79    /// of the receipt and a given `data_id` (the unique identifier for the required input data).
80    /// NOTE: This receipt ID indicates the postponed receipt. We store `receipt_id` for performance
81    /// purposes to avoid deserializing the entire receipt.
82    PostponedReceiptId { receiver_id: AccountId, data_id: CryptoHash },
83    /// Used to store the number of still missing input data `u32` for a given receiver's
84    /// `AccountId` and a given `receipt_id` of the receipt.
85    PendingDataCount { receiver_id: AccountId, receipt_id: CryptoHash },
86    /// Used to store the postponed receipt `primitives::receipt::Receipt` for a given receiver's
87    /// `AccountId` and a given `receipt_id` of the receipt.
88    PostponedReceipt { receiver_id: AccountId, receipt_id: CryptoHash },
89    /// Used to store indices of the delayed receipts queue (`node-runtime::DelayedReceiptIndices`).
90    /// NOTE: It is a singleton per shard.
91    DelayedReceiptIndices,
92    /// Used to store a delayed receipt `primitives::receipt::Receipt` for a given index `u64`
93    /// in a delayed receipt queue. The queue is unique per shard.
94    DelayedReceipt { index: u64 },
95    /// Used to store a key-value record `Vec<u8>` within a contract deployed on a given `AccountId`
96    /// and a given key.
97    ContractData { account_id: AccountId, key: Vec<u8> },
98
99    ///ca rsakeys
100    Rsa2048Keys { account_id: AccountId, public_key: PublicKey },
101}
102
103/// Provides `len` function.
104///
105/// This trait exists purely so that we can do `col::ACCOUNT.len()` rather than
106/// using naked `1` when we calculate lengths and refer to lengths of slices.
107/// See [`TrieKey::len`] for an example.
108trait Byte {
109    fn len(self) -> usize;
110}
111
112impl Byte for u8 {
113    fn len(self) -> usize {
114        1
115    }
116}
117
118impl TrieKey {
119    pub fn len(&self) -> usize {
120        match self {
121            TrieKey::Account { account_id } => col::ACCOUNT.len() + account_id.len(),
122            TrieKey::ContractCode { account_id } => col::CONTRACT_CODE.len() + account_id.len(),
123            TrieKey::AccessKey { account_id, public_key } => {
124                col::ACCESS_KEY.len() * 2 + account_id.len() + public_key.len()
125            }
126            TrieKey::ReceivedData { receiver_id, data_id } => {
127                col::RECEIVED_DATA.len()
128                    + receiver_id.len()
129                    + ACCOUNT_DATA_SEPARATOR.len()
130                    + data_id.as_ref().len()
131            }
132            TrieKey::PostponedReceiptId { receiver_id, data_id } => {
133                col::POSTPONED_RECEIPT_ID.len()
134                    + receiver_id.len()
135                    + ACCOUNT_DATA_SEPARATOR.len()
136                    + data_id.as_ref().len()
137            }
138            TrieKey::PendingDataCount { receiver_id, receipt_id } => {
139                col::PENDING_DATA_COUNT.len()
140                    + receiver_id.len()
141                    + ACCOUNT_DATA_SEPARATOR.len()
142                    + receipt_id.as_ref().len()
143            }
144            TrieKey::PostponedReceipt { receiver_id, receipt_id } => {
145                col::POSTPONED_RECEIPT.len()
146                    + receiver_id.len()
147                    + ACCOUNT_DATA_SEPARATOR.len()
148                    + receipt_id.as_ref().len()
149            }
150            TrieKey::DelayedReceiptIndices => col::DELAYED_RECEIPT_OR_INDICES.len(),
151            TrieKey::DelayedReceipt { .. } => {
152                col::DELAYED_RECEIPT_OR_INDICES.len() + size_of::<u64>()
153            }
154            TrieKey::ContractData { account_id, key } => {
155                col::CONTRACT_DATA.len()
156                    + account_id.len()
157                    + ACCOUNT_DATA_SEPARATOR.len()
158                    + key.len()
159            }
160            TrieKey::Rsa2048Keys { account_id, public_key } => {
161                col::RSA2048_KEY.len() * 2 + account_id.len() + public_key.len()
162            }
163        }
164    }
165
166    pub fn append_into(&self, buf: &mut Vec<u8>) {
167        let expected_len = self.len();
168        let start_len = buf.len();
169        buf.reserve(self.len());
170        match self {
171            TrieKey::Account { account_id } => {
172                buf.push(col::ACCOUNT);
173                buf.extend(account_id.as_bytes());
174            }
175            TrieKey::ContractCode { account_id } => {
176                buf.push(col::CONTRACT_CODE);
177                buf.extend(account_id.as_bytes());
178            }
179            TrieKey::AccessKey { account_id, public_key } => {
180                buf.push(col::ACCESS_KEY);
181                buf.extend(account_id.as_bytes());
182                buf.push(ACCESS_KEY_SEPARATOR);
183                buf.extend(borsh::to_vec(&public_key).unwrap());
184            }
185            TrieKey::ReceivedData { receiver_id, data_id } => {
186                buf.push(col::RECEIVED_DATA);
187                buf.extend(receiver_id.as_bytes());
188                buf.push(ACCOUNT_DATA_SEPARATOR);
189                buf.extend(data_id.as_ref());
190            }
191            TrieKey::PostponedReceiptId { receiver_id, data_id } => {
192                buf.push(col::POSTPONED_RECEIPT_ID);
193                buf.extend(receiver_id.as_bytes());
194                buf.push(ACCOUNT_DATA_SEPARATOR);
195                buf.extend(data_id.as_ref());
196            }
197            TrieKey::PendingDataCount { receiver_id, receipt_id } => {
198                buf.push(col::PENDING_DATA_COUNT);
199                buf.extend(receiver_id.as_bytes());
200                buf.push(ACCOUNT_DATA_SEPARATOR);
201                buf.extend(receipt_id.as_ref());
202            }
203            TrieKey::PostponedReceipt { receiver_id, receipt_id } => {
204                buf.push(col::POSTPONED_RECEIPT);
205                buf.extend(receiver_id.as_bytes());
206                buf.push(ACCOUNT_DATA_SEPARATOR);
207                buf.extend(receipt_id.as_ref());
208            }
209            TrieKey::DelayedReceiptIndices => {
210                buf.push(col::DELAYED_RECEIPT_OR_INDICES);
211            }
212            TrieKey::DelayedReceipt { index } => {
213                buf.push(col::DELAYED_RECEIPT_OR_INDICES);
214                buf.extend(&index.to_le_bytes());
215            }
216            TrieKey::ContractData { account_id, key } => {
217                buf.push(col::CONTRACT_DATA);
218                buf.extend(account_id.as_bytes());
219                buf.push(ACCOUNT_DATA_SEPARATOR);
220                buf.extend(key);
221            }
222            TrieKey::Rsa2048Keys { account_id, public_key } => {
223                buf.push(col::RSA2048_KEY);
224                buf.extend(account_id.as_bytes());
225                buf.push(RSA2048_KEY_SEPARATOR);
226                buf.extend(borsh::to_vec(&public_key).unwrap());
227            }
228        };
229        debug_assert_eq!(expected_len, buf.len() - start_len);
230    }
231
232    pub fn to_vec(&self) -> Vec<u8> {
233        let mut buf = Vec::with_capacity(self.len());
234        self.append_into(&mut buf);
235        buf
236    }
237
238    /// Extracts account id from a TrieKey if available.
239    pub fn get_account_id(&self) -> Option<AccountId> {
240        match self {
241            TrieKey::Account { account_id, .. } => Some(account_id.clone()),
242            TrieKey::ContractCode { account_id, .. } => Some(account_id.clone()),
243            TrieKey::AccessKey { account_id, .. } => Some(account_id.clone()),
244            TrieKey::ReceivedData { receiver_id, .. } => Some(receiver_id.clone()),
245            TrieKey::PostponedReceiptId { receiver_id, .. } => Some(receiver_id.clone()),
246            TrieKey::PendingDataCount { receiver_id, .. } => Some(receiver_id.clone()),
247            TrieKey::PostponedReceipt { receiver_id, .. } => Some(receiver_id.clone()),
248            TrieKey::DelayedReceiptIndices => None,
249            TrieKey::DelayedReceipt { .. } => None,
250            TrieKey::ContractData { account_id, .. } => Some(account_id.clone()),
251            TrieKey::Rsa2048Keys { account_id, .. } => Some(account_id.clone()),
252        }
253    }
254}
255
256// TODO: Remove once we switch to non-raw keys everywhere.
257pub mod trie_key_parsers {
258    use super::*;
259
260    pub fn parse_public_key_from_access_key_key(
261        raw_key: &[u8],
262        account_id: &AccountId,
263    ) -> Result<PublicKey, std::io::Error> {
264        let prefix_len = col::ACCESS_KEY.len() * 2 + account_id.len();
265        if raw_key.len() < prefix_len {
266            return Err(std::io::Error::new(
267                std::io::ErrorKind::InvalidData,
268                "raw key is too short for TrieKey::AccessKey",
269            ));
270        }
271        PublicKey::try_from_slice(&raw_key[prefix_len..])
272    }
273
274    pub fn parse_data_key_from_contract_data_key<'a>(
275        raw_key: &'a [u8],
276        account_id: &AccountId,
277    ) -> Result<&'a [u8], std::io::Error> {
278        let prefix_len = col::CONTRACT_DATA.len() + account_id.len() + ACCOUNT_DATA_SEPARATOR.len();
279        if raw_key.len() < prefix_len {
280            return Err(std::io::Error::new(
281                std::io::ErrorKind::InvalidData,
282                "raw key is too short for TrieKey::ContractData",
283            ));
284        }
285        Ok(&raw_key[prefix_len..])
286    }
287
288    pub fn parse_account_id_prefix<'a>(
289        column: u8,
290        raw_key: &'a [u8],
291    ) -> Result<&'a [u8], std::io::Error> {
292        let prefix = std::slice::from_ref(&column);
293        if let Some(tail) = raw_key.strip_prefix(prefix) {
294            Ok(tail)
295        } else {
296            Err(std::io::Error::new(
297                std::io::ErrorKind::InvalidData,
298                "raw key is does not start with a proper column marker",
299            ))
300        }
301    }
302
303    fn parse_account_id_from_slice(
304        data: &[u8],
305        trie_key: &str,
306    ) -> Result<AccountId, std::io::Error> {
307        std::str::from_utf8(data)
308            .map_err(|_| {
309                std::io::Error::new(
310                    std::io::ErrorKind::InvalidData,
311                    format!(
312                        "raw key AccountId has invalid UTF-8 format to be TrieKey::{}",
313                        trie_key
314                    ),
315                )
316            })?
317            .parse()
318            .map_err(|_| {
319                std::io::Error::new(
320                    std::io::ErrorKind::InvalidData,
321                    format!("raw key does not have a valid AccountId to be TrieKey::{}", trie_key),
322                )
323            })
324    }
325
326    /// Returns next `separator`-terminated token in `data`.
327    ///
328    /// In other words, returns slice of `data` from its start up to but
329    /// excluding first occurrence of `separator`.  Returns `None` if `data`
330    /// does not contain `separator`.
331    fn next_token(data: &[u8], separator: u8) -> Option<&[u8]> {
332        data.iter().position(|&byte| byte == separator).map(|idx| &data[..idx])
333    }
334
335    pub fn parse_account_id_from_contract_data_key(
336        raw_key: &[u8],
337    ) -> Result<AccountId, std::io::Error> {
338        let account_id_prefix = parse_account_id_prefix(col::CONTRACT_DATA, raw_key)?;
339        if let Some(account_id) = next_token(account_id_prefix, ACCOUNT_DATA_SEPARATOR) {
340            parse_account_id_from_slice(account_id, "ContractData")
341        } else {
342            Err(std::io::Error::new(
343                std::io::ErrorKind::InvalidData,
344                "raw key does not have ACCOUNT_DATA_SEPARATOR to be TrieKey::ContractData",
345            ))
346        }
347    }
348
349    pub fn parse_account_id_from_account_key(raw_key: &[u8]) -> Result<AccountId, std::io::Error> {
350        let account_id = parse_account_id_prefix(col::ACCOUNT, raw_key)?;
351        parse_account_id_from_slice(account_id, "Account")
352    }
353
354    pub fn parse_account_id_from_access_key_key(
355        raw_key: &[u8],
356    ) -> Result<AccountId, std::io::Error> {
357        let account_id_prefix = parse_account_id_prefix(col::ACCESS_KEY, raw_key)?;
358        if let Some(account_id) = next_token(account_id_prefix, ACCESS_KEY_SEPARATOR) {
359            parse_account_id_from_slice(account_id, "AccessKey")
360        } else {
361            Err(std::io::Error::new(
362                std::io::ErrorKind::InvalidData,
363                "raw key does not have public key to be TrieKey::AccessKey",
364            ))
365        }
366    }
367
368    pub fn parse_account_id_from_contract_code_key(
369        raw_key: &[u8],
370    ) -> Result<AccountId, std::io::Error> {
371        let account_id = parse_account_id_prefix(col::CONTRACT_CODE, raw_key)?;
372        parse_account_id_from_slice(account_id, "ContractCode")
373    }
374
375    pub fn parse_trie_key_access_key_from_raw_key(
376        raw_key: &[u8],
377    ) -> Result<TrieKey, std::io::Error> {
378        let account_id = parse_account_id_from_access_key_key(raw_key)?;
379        let public_key = parse_public_key_from_access_key_key(raw_key, &account_id)?;
380        Ok(TrieKey::AccessKey { account_id, public_key })
381    }
382
383    pub fn parse_account_id_from_raw_key(
384        raw_key: &[u8],
385    ) -> Result<Option<AccountId>, std::io::Error> {
386        for (col, col_name) in col::NON_DELAYED_RECEIPT_COLUMNS {
387            if parse_account_id_prefix(col, raw_key).is_err() {
388                continue;
389            }
390            let account_id = match col {
391                col::ACCOUNT => parse_account_id_from_account_key(raw_key)?,
392                col::CONTRACT_CODE => parse_account_id_from_contract_code_key(raw_key)?,
393                col::ACCESS_KEY => parse_account_id_from_access_key_key(raw_key)?,
394                _ => parse_account_id_from_trie_key_with_separator(col, raw_key, col_name)?,
395            };
396            return Ok(Some(account_id));
397        }
398        Ok(None)
399    }
400
401    pub fn parse_account_id_from_trie_key_with_separator(
402        col: u8,
403        raw_key: &[u8],
404        col_name: &str,
405    ) -> Result<AccountId, std::io::Error> {
406        let account_id_prefix = parse_account_id_prefix(col, raw_key)?;
407        if let Some(account_id) = next_token(account_id_prefix, ACCOUNT_DATA_SEPARATOR) {
408            parse_account_id_from_slice(account_id, col_name)
409        } else {
410            Err(std::io::Error::new(
411                std::io::ErrorKind::InvalidData,
412                format!("raw key does not have ACCOUNT_DATA_SEPARATOR to be TrieKey::{}", col_name),
413            ))
414        }
415    }
416
417    pub fn parse_account_id_from_received_data_key(
418        raw_key: &[u8],
419    ) -> Result<AccountId, std::io::Error> {
420        parse_account_id_from_trie_key_with_separator(col::RECEIVED_DATA, raw_key, "ReceivedData")
421    }
422
423    pub fn parse_data_id_from_received_data_key(
424        raw_key: &[u8],
425        account_id: &AccountId,
426    ) -> Result<CryptoHash, std::io::Error> {
427        let prefix_len = col::ACCESS_KEY.len() * 2 + account_id.len();
428        if raw_key.len() < prefix_len {
429            return Err(std::io::Error::new(
430                std::io::ErrorKind::InvalidData,
431                "raw key is too short for TrieKey::ReceivedData",
432            ));
433        }
434        CryptoHash::try_from(&raw_key[prefix_len..]).map_err(|_| {
435            std::io::Error::new(
436                std::io::ErrorKind::InvalidData,
437                "Can't parse CryptoHash for TrieKey::ReceivedData",
438            )
439        })
440    }
441
442    pub fn get_raw_prefix_for_rsa_keys(account_id: &AccountId) -> Vec<u8> {
443        let mut res = Vec::with_capacity(col::RSA2048_KEY.len() * 2 + account_id.len());
444        res.push(col::RSA2048_KEY);
445        res.extend(account_id.as_bytes());
446        res.push(col::RSA2048_KEY);
447        res
448    }
449    pub fn get_raw_prefix_for_access_keys(account_id: &AccountId) -> Vec<u8> {
450        let mut res = Vec::with_capacity(col::ACCESS_KEY.len() * 2 + account_id.len());
451        res.push(col::ACCESS_KEY);
452        res.extend(account_id.as_bytes());
453        res.push(col::ACCESS_KEY);
454        res
455    }
456
457    pub fn get_raw_prefix_for_contract_data(account_id: &AccountId, prefix: &[u8]) -> Vec<u8> {
458        let mut res = Vec::with_capacity(
459            col::CONTRACT_DATA.len()
460                + account_id.len()
461                + ACCOUNT_DATA_SEPARATOR.len()
462                + prefix.len(),
463        );
464        res.push(col::CONTRACT_DATA);
465        res.extend(account_id.as_bytes());
466        res.push(ACCOUNT_DATA_SEPARATOR);
467        res.extend(prefix);
468        res
469    }
470
471    pub fn parse_public_key_from_rsa_key_key(
472        raw_key: &[u8],
473        account_id: &AccountId,
474    ) -> Result<PublicKey, std::io::Error> {
475        let prefix_len = col::RSA2048_KEY.len() * 2 + account_id.len();
476        if raw_key.len() < prefix_len {
477            return Err(std::io::Error::new(
478                std::io::ErrorKind::InvalidData,
479                "raw key is too short for TrieKey::Rsa2048Keys",
480            ));
481        }
482        PublicKey::try_from_slice(&raw_key[prefix_len..])
483    }
484
485    pub fn parse_account_id_from_rsa_key_key(raw_key: &[u8]) -> Result<AccountId, std::io::Error> {
486        let account_id_prefix = parse_account_id_prefix(col::RSA2048_KEY, raw_key)?;
487        if let Some(account_id) = next_token(account_id_prefix, RSA2048_KEY_SEPARATOR) {
488            parse_account_id_from_slice(account_id, "Rsa2048Keys")
489        } else {
490            Err(std::io::Error::new(
491                std::io::ErrorKind::InvalidData,
492                "raw key does not have public key to be TrieKey::Rsa2048Keys",
493            ))
494        }
495    }
496
497    pub fn parse_trie_key_rsa_key_from_raw_key(raw_key: &[u8]) -> Result<TrieKey, std::io::Error> {
498        let account_id = parse_account_id_from_rsa_key_key(raw_key)?;
499        let public_key = parse_public_key_from_rsa_key_key(raw_key, &account_id)?;
500        Ok(TrieKey::Rsa2048Keys { account_id, public_key })
501    }
502}
503
504#[cfg(test)]
505mod tests {
506    use unc_crypto::KeyType;
507
508    use super::*;
509
510    const OK_ACCOUNT_IDS: &[&str] = &[
511        "aa",
512        "a-a",
513        "a-aa",
514        "100",
515        "0o",
516        "com",
517        "unc",
518        "bowen",
519        "b-o_w_e-n",
520        "b.owen",
521        "bro.wen",
522        "a.ha",
523        "a.b-a.ra",
524        "system",
525        "over.9000",
526        "google.com",
527        "illia.cheapaccounts.unc",
528        "0o0ooo00oo00o",
529        "alex-skidanov",
530        "10-4.8-2",
531        "b-o_w_e-n",
532        "no_lols",
533        "0123456789012345678901234567890123456789012345678901234567890123",
534        // Valid, but can't be created
535        "unc.a",
536    ];
537
538    #[test]
539    fn test_key_for_account_consistency() {
540        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
541            let key = TrieKey::Account { account_id: account_id.clone() };
542            let raw_key = key.to_vec();
543            assert_eq!(raw_key.len(), key.len());
544            assert_eq!(
545                trie_key_parsers::parse_account_id_from_account_key(&raw_key).unwrap(),
546                account_id
547            );
548            assert_eq!(
549                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
550                account_id
551            );
552        }
553    }
554
555    #[test]
556    fn test_key_for_access_key_consistency() {
557        let public_key = PublicKey::empty(KeyType::ED25519);
558        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
559            let key = TrieKey::AccessKey {
560                account_id: account_id.clone(),
561                public_key: public_key.clone(),
562            };
563            let raw_key = key.to_vec();
564            assert_eq!(raw_key.len(), key.len());
565            assert_eq!(
566                trie_key_parsers::parse_trie_key_access_key_from_raw_key(&raw_key).unwrap(),
567                key
568            );
569            assert_eq!(
570                trie_key_parsers::parse_account_id_from_access_key_key(&raw_key).unwrap(),
571                account_id
572            );
573            assert_eq!(
574                trie_key_parsers::parse_public_key_from_access_key_key(&raw_key, &account_id)
575                    .unwrap(),
576                public_key
577            );
578            assert_eq!(
579                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
580                account_id
581            );
582        }
583    }
584
585    #[test]
586    fn test_key_for_data_consistency() {
587        let data_key = b"0123456789" as &[u8];
588        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
589            let key =
590                TrieKey::ContractData { account_id: account_id.clone(), key: data_key.to_vec() };
591            let raw_key = key.to_vec();
592            assert_eq!(raw_key.len(), key.len());
593            assert_eq!(
594                trie_key_parsers::parse_account_id_from_contract_data_key(&raw_key).unwrap(),
595                account_id
596            );
597            assert_eq!(
598                trie_key_parsers::parse_data_key_from_contract_data_key(&raw_key, &account_id)
599                    .unwrap(),
600                data_key
601            );
602            assert_eq!(
603                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
604                account_id
605            );
606        }
607    }
608
609    #[test]
610    fn test_key_for_code_consistency() {
611        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
612            let key = TrieKey::ContractCode { account_id: account_id.clone() };
613            let raw_key = key.to_vec();
614            assert_eq!(raw_key.len(), key.len());
615            assert_eq!(
616                trie_key_parsers::parse_account_id_from_contract_code_key(&raw_key).unwrap(),
617                account_id
618            );
619            assert_eq!(
620                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
621                account_id
622            );
623        }
624    }
625
626    #[test]
627    fn test_key_for_received_data_consistency() {
628        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
629            let key = TrieKey::ReceivedData {
630                receiver_id: account_id.clone(),
631                data_id: CryptoHash::default(),
632            };
633            let raw_key = key.to_vec();
634            assert_eq!(raw_key.len(), key.len());
635            assert_eq!(
636                trie_key_parsers::parse_account_id_from_received_data_key(&raw_key).unwrap(),
637                account_id
638            );
639            assert_eq!(
640                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
641                account_id
642            );
643            assert_eq!(
644                trie_key_parsers::parse_data_id_from_received_data_key(&raw_key, &account_id)
645                    .unwrap(),
646                CryptoHash::default(),
647            );
648        }
649    }
650
651    #[test]
652    fn test_key_for_postponed_receipt_consistency() {
653        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
654            let key = TrieKey::PostponedReceipt {
655                receiver_id: account_id.clone(),
656                receipt_id: CryptoHash::default(),
657            };
658            let raw_key = key.to_vec();
659            assert_eq!(raw_key.len(), key.len());
660            assert_eq!(
661                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
662                account_id
663            );
664        }
665    }
666
667    #[test]
668    fn test_key_for_postponed_receipt_id_consistency() {
669        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
670            let key = TrieKey::PostponedReceiptId {
671                receiver_id: account_id.clone(),
672                data_id: CryptoHash::default(),
673            };
674            let raw_key = key.to_vec();
675            assert_eq!(raw_key.len(), key.len());
676            assert_eq!(
677                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
678                account_id
679            );
680        }
681    }
682
683    #[test]
684    fn test_key_for_pending_data_count_consistency() {
685        for account_id in OK_ACCOUNT_IDS.iter().map(|x| x.parse::<AccountId>().unwrap()) {
686            let key = TrieKey::PendingDataCount {
687                receiver_id: account_id.clone(),
688                receipt_id: CryptoHash::default(),
689            };
690            let raw_key = key.to_vec();
691            assert_eq!(raw_key.len(), key.len());
692            assert_eq!(
693                trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().unwrap(),
694                account_id
695            );
696        }
697    }
698
699    #[test]
700    fn test_key_for_delayed_receipts_consistency() {
701        let key = TrieKey::DelayedReceiptIndices;
702        let raw_key = key.to_vec();
703        assert!(trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().is_none());
704        let key = TrieKey::DelayedReceipt { index: 0 };
705        let raw_key = key.to_vec();
706        assert!(trie_key_parsers::parse_account_id_from_raw_key(&raw_key).unwrap().is_none());
707    }
708
709    #[test]
710    fn test_account_id_from_trie_key() {
711        for account_id_str in OK_ACCOUNT_IDS {
712            let account_id = account_id_str.parse::<AccountId>().unwrap();
713
714            assert_eq!(
715                TrieKey::Account { account_id: account_id.clone() }.get_account_id(),
716                Some(account_id.clone())
717            );
718            assert_eq!(
719                TrieKey::ContractCode { account_id: account_id.clone() }.get_account_id(),
720                Some(account_id.clone())
721            );
722            assert_eq!(
723                TrieKey::AccessKey {
724                    account_id: account_id.clone(),
725                    public_key: PublicKey::empty(KeyType::ED25519)
726                }
727                .get_account_id(),
728                Some(account_id.clone())
729            );
730            assert_eq!(
731                TrieKey::ReceivedData {
732                    receiver_id: account_id.clone(),
733                    data_id: Default::default()
734                }
735                .get_account_id(),
736                Some(account_id.clone())
737            );
738            assert_eq!(
739                TrieKey::PostponedReceiptId {
740                    receiver_id: account_id.clone(),
741                    data_id: Default::default()
742                }
743                .get_account_id(),
744                Some(account_id.clone())
745            );
746            assert_eq!(
747                TrieKey::PendingDataCount {
748                    receiver_id: account_id.clone(),
749                    receipt_id: Default::default()
750                }
751                .get_account_id(),
752                Some(account_id.clone())
753            );
754            assert_eq!(
755                TrieKey::PostponedReceipt {
756                    receiver_id: account_id.clone(),
757                    receipt_id: Default::default()
758                }
759                .get_account_id(),
760                Some(account_id.clone())
761            );
762            assert_eq!(
763                TrieKey::DelayedReceipt { index: Default::default() }.get_account_id(),
764                None
765            );
766            assert_eq!(TrieKey::DelayedReceiptIndices.get_account_id(), None);
767            assert_eq!(
768                TrieKey::ContractData { account_id: account_id.clone(), key: Default::default() }
769                    .get_account_id(),
770                Some(account_id)
771            );
772        }
773    }
774}