Migrating to latest version of ic_stable_structures (0.6.2)

Migrating to the latest version of ic_stable_structures panics due to max_size being greater than actual size of the struct. I have explained the problem in detail below

I had a struct WasmType which looked like this:

#[derive(Clone, Serialize, Deserialize, PartialEq, PartialOrd, Eq, Ord, CandidType)]
pub enum WasmType {
    SubnetOrchestratorWasm,
    IndividualUserWasm,
    PostCacheWasm
}

impl Storable for WasmType {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        let mut bytes = vec![];
        ciborium::ser::into_writer(self, &mut bytes).unwrap();
        Cow::Owned(bytes)
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        let wasm_type: WasmType = de::from_reader(bytes.as_ref()).unwrap();
        wasm_type
    }
}

impl BoundedStorable for WasmType {
    const MAX_SIZE: u32 = 100;

    const IS_FIXED_SIZE: bool = true;
}

This worked perfectly fine.

Once I migrated to latest version of ic-stable-structures (0.6.2) I reimplemented the trait Storable to include Bound and removed the implementation of BoundedStorable

impl Storable for WasmType {
    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
        let mut bytes = vec![];
        ciborium::ser::into_writer(self, &mut bytes).unwrap();
        Cow::Owned(bytes)
    }

    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
        let wasm_type: WasmType = de::from_reader(bytes.as_ref()).unwrap();
        wasm_type
    }

    const BOUND: Bound = Bound::Bounded { max_size: 100, is_fixed_size: true };
}

Using this again with stable_structures panics with error

Panicked at 'assertion `left == right` failed: expected a fixed-size element with length 100 bytes, but found 19 bytes\n

isn’t max_size an upper bound for the size of the struct?

1 Like

If is_fixed_size is set to true, that means that every single element must be exactly of length max_size (i.e. 100 bytes). If max_size is simply a cap and elements can be <= 100 bytes, then is_fixed_size should be false. The error you’re seeing is exactly what I would expect.

What is surprising to me is that you’re saying that in the previous version this was working. Are you sure in your previous code that was deployed to production you had both MAX_SIZE = 100 and IS_FIXED_SIZE = true?

Yes.

We are using version 0.5.6.

I assumed max_size is an upper limit. So every value of type WasmType would be of fixed size and none of them would exceed 100 bytes limit

I narrowed it down to this commit where we introduced more rigorous checks to ensure developers aren’t specifying incorrect bounds, as is the case here.

Your current configuration is definitely incorrect, but the only real consequence of it is that you’re allocating more memory than you need.

The easiest path forward, if you’re keen on upgrading, would be a migration. How big is the data? If it’s small enough that it can serialized within a single message, then migration can be relatively simple:

  1. Move the map temporarily to the heap.
  2. Upgrade stable structures
  3. Create a new StableBTreeMap
  4. Move the data back
1 Like

The data is small. I will follow the steps that you suggested. Thank you so much

2 Likes