Mykle Hansen
Mykle Hansen

Reputation: 552

How to implement multidimensional hash in Near contract

In a Near smart contract written in Rust, I'm trying to store a hash of sets of 'seedID's, indexed by two keys 'vtype' and 'vsubtype'. I'm using Near's key-indexed collections UnorderedMap and UnorderedSet, like so:

    // a group of seed IDs
pub type SeedIdSet = UnorderedSet<SeedId>;
    // a map from vsubtype to seedIdSet
pub type SeedSubIndex = UnorderedMap< VSubType, SeedIdSet >;
    // an array of those, indexed by vtype 
pub type SeedIndex = [SeedSubIndex; 3];

The SeedIndex type is what I'm trying to store on the chain. But I'm getting errors about unimplemented traits when I try to compile this:

error[E0277]: the trait bound `UnorderedMap<u8, UnorderedSet<u64>>: Default` is not satisfied
   --> src/lib.rs:496:10
    |
496 | #[derive(BorshDeserialize, BorshSerialize)]
    |          ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `UnorderedMap<u8, UnorderedSet<u64>>`
    |
    = note: required because of the requirements on the impl of `BorshDeserialize` for `[UnorderedMap<u8, UnorderedSet<u64>>; 3]`
    = help: see issue #48214
    = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `UnorderedMap<u8, UnorderedSet<u64>>: Copy` is not satisfied
   --> src/lib.rs:496:10
    |
496 | #[derive(BorshDeserialize, BorshSerialize)]
    |          ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `UnorderedMap<u8, UnorderedSet<u64>>`
    |
    = note: required because of the requirements on the impl of `BorshDeserialize` for `[UnorderedMap<u8, UnorderedSet<u64>>; 3]`
    = help: see issue #48214
    = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

I'm not sure how to debug this. Is it because something is missing in the UnorderedMap implementation? Is nesting them just not allowed? Is there something wrong about my whole approach? Much obliged for any advice.

Upvotes: 1

Views: 170

Answers (1)

Evgeny Kuzyakov
Evgeny Kuzyakov

Reputation: 1078

There are 2 options:

  1. You don't need iterators. You can use composed key, e.g.:
struct Key {
  key_a: String,
  key_b: String,
}

kv: LookupMap<Key, Value>;
  1. You need iterators. Then you need to initialize inner maps yourself:
pub struct Contract {
   pub outer_map: UnorderedMap<OuterKey, InnerMap>, 
}

pub struct InnerMap {
   pub inner_map: UnorderedMap<InnerKey, Value>, 
}

impl InnerMap {
  pub fn new(outer_key: OuterKey) -> Self {
     let mut inner_map_prefix = b"i".to_vec();
     // Hash outer key to create a unique prefix for this inner map.
     inner_map_prefix.extend(env::sha256(outer_key));
     Self {
       inner_map: UnorderedMap::new(inner_map_prefix)
     }
  }   
}

You can find an example of this in the old fungible token implementation: https://github.com/near/near-sdk-rs/blob/1d3535bd131b68f97a216e643ad1cba19e16dddf/examples/fungible-token/src/lib.rs#L30-L45

Upvotes: 2

Related Questions