关于near:关于NEAR-collections-的思考笔记

链上状态存储

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理