乐趣区

关于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,以防止出错
退出移动版