Principal size for stable structure

What’s the size of a Principal that should be reserved when defining a bounded size with the stable-structures?

 pub struct AnalyticKey {
        pub satellite_id: Principal, // <--- what's the size to reserve again?
        pub key: String,
        pub session_id: String,
    }

impl BoundedStorable for AnalyticKey {
     // TODO: replace with size_of::<String>() as u32 + size_of::<String>() as u32 + ????
    const MAX_SIZE: u32 = size_of::<AnalyticKey>() as u32;
    const IS_FIXED_SIZE: bool = false;
}

29 bytes

UPDATE: it’s indeed 29 but, see Convert principal to Vec 29 bytes length - #3 by levi for implementation which requires one extra byte.

1 Like

Thanks @LightningLad91 for the anwer! You are right 29 bytes.

I searched in the doc too but, first occurence lead me to the glossary https://internetcomputer.org/docs/current/references/glossary/#principal which does not reference the size nor the other documentation page.

So I asked on the forum and only later gave another shot to the documentation :wink:.

1 Like

Btw. - assuming size_of cannot be used to determine the size - is there a constant in Rust for these 29 bytes size of a Principal?

Something fishy.

I define const MAX_SIZE: u32 = (size_of::<String>() as u32 * 2) + 29; which equals 77 but, when I compile the canister and give a try I get an error message Expected <= 53 bytes, found 82 bytes at runtime.

Are you possibly looking at the textual encoding of the principal rather than the raw value? Or maybe it’s the candid encoding? I know if I want the raw principal value as a blob I have to do Principal.toBlob() in Motoko.

Might good be! Thanks for the input.

What’s weird is that if I print out following in the Rust playground

fn main() {
    println!("Size {:?}", (size_of::<String>() as u32 * 2) + 29);
}

I get 77 as a result. However, when I use the exact same code - (size_of::<String>() as u32 * 2) + 29 - to define my implementation for my struct in my stable-structures, I get the error max size limit 53 at runtime.

Been coding since 6am today and it’s 10pm, so I probably need to go to sleep and have another look tomorrow again, which I’ll do :grin:.

1 Like

I don’t use rust so I’m not sure exactly what’s going on but If I’m understanding correctly it looks like there’s a 5 byte difference (82 - 77) between what you expect the size to be and the actual size of the serialized structure. I brought up candid encoding before because it adds some bytes to the data being encoded. I believe it adds characters ”DIDL” plus a type tag and was wondering if that could be the 5 bytes that isn’t being accounted for with the 29 bytes.

If the principal is being encoded with candid then it could have up to 34 bytes total (I think).

1 Like

Oh that’s an excellent point, thanks for the input :+1:! I’ll give a try and report.

You were right @LightningLad91, the remaining issue I had was related to the Candid encoding. It adds the ”DIDL” but also serialize values and types (from what I understand from it source code).

pub fn serialize<W: io::Write>(&mut self, mut writer: W) -> Result<()> {
        writer.write_all(b"DIDL")?;
        self.type_ser.serialize()?;
        writer.write_all(self.type_ser.get_result())?;
        writer.write_all(self.value_ser.get_result())?;
        Ok(())
    }

So in addition to defining a proper size for the principal, I also replaced Candid serialization for a custom manual serialization as they do in Internet Idenditiy: https://github.com/dfinity/internet-identity/blob/0641ea641f64a2b51b3647ad1140840c2118b5d6/src/archive/src/main.rs#L212

When it comes to the Principal I ultimately actually serialized it to a bytes length of 30 (29 bytes + one for the length). See this answer for the reason why and how: Convert principal to Vec 29 bytes length - #3 by levi

1 Like

Thanks a lot for the brainstorming and inputs @LightningLad91!