diem_types/
access_path.rs

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