链上状态存储
链上状态均以 key – value 键值对模式存储,通常包含「合约状态」和「容器状态」,调用合约办法时会反序列化「合约状态」到内存,但不会反序列化「容器状态」,合约办法调用结束后会序列化「合约状态」回链上
-
合约状态:指 near_bindgen 润饰的 Contract 对象存储的链上状态,也是 env::state_read 函数返回的状态,以一对 key – value 键值对模式存储 Contract 对象内的数据,并且 key 的值为 ‘STATE’,对应的 base64 值为 ‘U1RBVEU=’
// alice #[near_bindgen] #[derive(BorshDeserialize,BorshSerialize)] struct Contract {} impl Default for Contract {fn default() -> Self {Self {} } }
near view-state alice --finality final [{ key: 'U1RBVEU='', value: ''} //「合约状态」为空,因为 Contract 对象内没有属性 ]
-
容器状态:指应用 near_sdk::collections 存储的链上状态,以多对 key – value 键值对模式存储容器内数据,除了合约状态以外剩下的通常都是容器状态
// bob #[near_bindgen] #[derive(BorshDeserialize,BorshSerialize,PanicOnDefault)] struct Contract {vec: Vector<String>} #[near_bindgen] impl Contract {#[init] #[private] pub fn init() -> Self { Self {vec: Vector::new(b'v') } } pub fn add(&mut self, value: String) {self.vec.push(&value); } }
near call bob init --account-id bob near call bob add '{"value":"hello"}' --account-id bob near call bob add '{"value":"world"}' --account-id bob near view-state bob --finality final [{ key: 'U1RBVEU=', value: 'AgAAAAAAAAABAAAAdg=='}, //「合约状态」不为空,包含了 Vector,不包含 Vector 内的数据 {key: 'dgAAAAAAAAAA', value: 'BQAAAGhlbGxv'}, //「容器状态」,Vector 中存储的 "hello" {key: 'dgEAAAAAAAAA', value: 'BQAAAHdvcmxk'} //「容器状态」,Vector 中存储的 "world" ]
collections
根底 collections
-
LookupMap
* 无奈迭代 * 大量数据且无需迭代的状况下应用 每存一份数据: KEY, VALUE 在链上对应存储: {key: key_prefix + KEY, value: VALUE}
-
LookupSet
* 无奈迭代 * 大量数据且无需迭代的状况下应用 每存一份数据: VALUE 在链上对应存储: {key: key_prefix + VALUE, value: VALUE}
-
Vector
* 能够迭代 * 无奈排序 * 大量数据且无需排序的状况下应用 每存一份数据: VALUE 在链上对应存储: {key: key_prefix + VALUE_INDEX, value: VALUE}
-
LazyOption
* 不常常从链上反序列化的状况下应用 * 所有 collections 实质上都是 LazyOption 保留的数据: VALUE 在链上对应存储: {key: key_prefix, value: VALUE}
派生 collections
-
UnorderedSet
* 能够迭代 * 大量数据且须要迭代的状况下应用 每存一份数据: VALUE 在链上对应存储: {key: key_prefix + 'i' + VALUE, value: VALUE_INDEX} // LookupMap for VALUE_INDEX {key: key_prefix + 'e' + VALUE_INDEX, value: VALUE} // Vector for VALUE UnorderedSet 基于一个 Vector 实现,用于存储 VALUE,其中 VALUE 在 Vector 中的索引值 VALUE_INDEX 也以 key - value 键值对模式间接存储在链上(相当于一个 LookupMap)。UnoederedSet 的存储占用为 LookupSet 的两倍
-
UnorderedMap
* 能够迭代 * 大量数据且须要迭代的状况下应用 每存一份数据: KEY, VALUE 在链上对应存储: {key: key_prefix + 'i' + KEY, value: KEY_INDEX} // LookupMap for KEY_INDEX {key: key_prefix + 'k' + KEY_INDEX, value: KEY} // Vector for KEY {key: key_prefix + 'v' + KEY_INDEX, value: VALUE} // Vector for VALUE UnorderedMap 基于两个 Vector 实现,别离用于存储 KEY 和 VALUE,其中 KEY 在 Vector 中的索引值 KEY_INDEX 也以 key - value 键值对模式间接存储在链上(相当于一个 LookupMap)。UnoederedMap 的存储占用为 LookupMap 的三倍
-
TreeMap
* 能够迭代 * 大量数据且须要迭代的状况下应用 每存一份数据: KEY, VALUE 在链上对应存储: {key: key_prefix + 'v' + KEY, value: VALUE} // LookupMap for VALUE {key: key_prefix + 'n' + NODE_INDEX, value: NODE<KEY>} // Vector for NODE<KEY> TreeMap 基于一个 LookupMap 和一个 Vector 实现,LookupMap 用于间接存储 KEY 和 VALUE,Vector 用于存储一棵 AVL 树,树的节点保留 KEY。TreeMap 插入删除效率不如 UnorderedMap 但存储占用比 UnorderedMap 更小
collections 注意事项
-
collections 中所有 get 办法都是间接从链上反序列化数据到内存,因而返回的是
Option<T>
而不是
Option<&T>
批改完的数据须要从新 insert 或 replace 回 collections
-
当 collection 嵌套 collection,如:
#[near_bindgen] #[derive(BorshDeserialize,BorshSerialize)] struct Contract {data: LookupMap<String, Vector<String>>}
- 拿到 Vector 中的数据批改完后须要 replace Vector 中原来的数据,但 Vector 不须要 insert 回 LookupMap
- 向 Vector push 或 remove 数据后,须要将 Vector insert 回 LookupMap,因为 Vector 的长度产生了扭转,Vector 的属性通过 LookupMap 存储在链上的一对 key – value 中,须要批改这对 key – value,否则会呈现状态不统一。 倡议无论是批改数据还是新增、删除数据,都将 Vector insert 回 LookupMap,以防止出错
-