链上状态存储

链上状态均以 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 bobnear call bob add '{"value":"hello"}' --account-id bobnear call bob add '{"value":"world"}' --account-id bobnear 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 VALUEUnorderedSet 基于一个 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 VALUEUnorderedMap 基于两个 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>>}
      1. 拿到 Vector 中的数据批改完后须要 replace Vector 中原来的数据,但 Vector 不须要 insert 回 LookupMap
      2. 向 Vector push 或 remove 数据后,须要将 Vector insert 回 LookupMap,因为 Vector 的长度产生了扭转,Vector 的属性通过 LookupMap 存储在链上的一对 key - value 中,须要批改这对 key - value,否则会呈现状态不统一。倡议无论是批改数据还是新增、删除数据,都将 Vector insert 回 LookupMap,以防止出错