Skip to main content

diem_types/
access_path.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Suppose we have the following data structure in a smart contract:
5//!
6//! struct B {
7//!   Map<String, String> mymap;
8//! }
9//!
10//! struct A {
11//!   B b;
12//!   int my_int;
13//! }
14//!
15//! struct C {
16//!   List<int> mylist;
17//! }
18//!
19//! A a;
20//! C c;
21//!
22//! and the data belongs to Alice. Then an access to `a.b.mymap` would be translated to an access
23//! to an entry in key-value store whose key is `<Alice>/a/b/mymap`. In the same way, the access to
24//! `c.mylist` would need to query `<Alice>/c/mylist`.
25//!
26//! So an account stores its data in a directory structure, for example:
27//!   <Alice>/balance:   10
28//!   <Alice>/a/b/mymap: {"Bob" => "abcd", "Carol" => "efgh"}
29//!   <Alice>/a/myint:   20
30//!   <Alice>/c/mylist:  [3, 5, 7, 9]
31//!
32//! If someone needs to query the map above and find out what value associated with "Bob" is,
33//! `address` will be set to Alice and `path` will be set to "/a/b/mymap/Bob".
34//!
35//! On the other hand, if you want to query only <Alice>/a/*, `address` will be set to Alice and
36//! `path` will be set to "/a" and use the `get_prefix()` method from statedb
37
38use crate::account_address::AccountAddress;
39use diem_crypto::hash::HashValue;
40use move_core_types::language_storage::{ModuleId, ResourceKey, StructTag, CODE_TAG, RESOURCE_TAG};
41#[cfg(any(test, feature = "fuzzing"))]
42use proptest_derive::Arbitrary;
43use serde::{Deserialize, Serialize};
44use std::{convert::TryFrom, fmt};
45
46#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
47#[cfg_attr(any(test, feature = "fuzzing"), derive(Arbitrary))]
48pub struct AccessPath {
49    pub address: AccountAddress,
50    #[serde(with = "serde_bytes")]
51    pub path: Vec<u8>,
52}
53
54#[derive(Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
55pub enum Path {
56    Code(ModuleId),
57    Resource(StructTag),
58}
59
60impl AccessPath {
61    pub fn new(address: AccountAddress, path: Vec<u8>) -> Self {
62        AccessPath { address, path }
63    }
64
65    pub fn resource_access_vec(tag: StructTag) -> Vec<u8> {
66        bcs::to_bytes(&Path::Resource(tag)).expect("Unexpected serialization error")
67    }
68
69    /// Convert Accesses into a byte offset which would be used by the storage layer to resolve
70    /// where fields are stored.
71    pub fn resource_access_path(key: ResourceKey) -> AccessPath {
72        let path = AccessPath::resource_access_vec(key.type_);
73        AccessPath {
74            address: key.address,
75            path,
76        }
77    }
78
79    fn code_access_path_vec(key: ModuleId) -> Vec<u8> {
80        bcs::to_bytes(&Path::Code(key)).expect("Unexpected serialization error")
81    }
82
83    pub fn code_access_path(key: ModuleId) -> AccessPath {
84        let address = *key.address();
85        let path = AccessPath::code_access_path_vec(key);
86        AccessPath { address, path }
87    }
88
89    /// Extract the structured resource or module `Path` from `self`
90    pub fn get_path(&self) -> Path {
91        bcs::from_bytes::<Path>(&self.path).expect("Unexpected serialization error")
92    }
93
94    /// Extract a StructTag from `self`. Returns Some if this is a resource access
95    /// path and None otherwise
96    pub fn get_struct_tag(&self) -> Option<StructTag> {
97        match self.get_path() {
98            Path::Resource(s) => Some(s),
99            Path::Code(_) => None,
100        }
101    }
102}
103
104impl fmt::Debug for AccessPath {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
106        write!(
107            f,
108            "AccessPath {{ address: {:x}, path: {} }}",
109            self.address,
110            hex::encode(&self.path)
111        )
112    }
113}
114
115impl fmt::Display for AccessPath {
116    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117        if self.path.len() < 1 + HashValue::LENGTH {
118            write!(f, "{:?}", self)
119        } else {
120            write!(f, "AccessPath {{ address: {:x}, ", self.address)?;
121            match self.path[0] {
122                RESOURCE_TAG => write!(f, "type: Resource, ")?,
123                CODE_TAG => write!(f, "type: Module, ")?,
124                tag => write!(f, "type: {:?}, ", tag)?,
125            };
126            write!(
127                f,
128                "hash: {:?}, ",
129                hex::encode(&self.path[1..=HashValue::LENGTH])
130            )?;
131            write!(
132                f,
133                "suffix: {:?} }} ",
134                String::from_utf8_lossy(&self.path[1 + HashValue::LENGTH..])
135            )
136        }
137    }
138}
139
140impl From<&ModuleId> for AccessPath {
141    fn from(id: &ModuleId) -> AccessPath {
142        AccessPath {
143            address: *id.address(),
144            path: id.access_vector(),
145        }
146    }
147}
148
149impl TryFrom<&[u8]> for Path {
150    type Error = bcs::Error;
151
152    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
153        bcs::from_bytes::<Path>(bytes)
154    }
155}
156
157impl TryFrom<&Vec<u8>> for Path {
158    type Error = bcs::Error;
159
160    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
161        bcs::from_bytes::<Path>(bytes)
162    }
163}