Skip to content

State

Integers are stored as little-endian. Signed integers are stored as two's complement. Note that lengths are also stored as little-endian.

State

::=

0xnn => u8/i8

(i8 is two's complement)

| 0xnn×2 => u16/i16

(little-endian, i16 is two's complement)

| 0xnn×4 => u32/i32

(little-endian, i32 is two's complement)

| 0xnn×8 => u64/i64

(little-endian, i64 is two's complement)

| 0xnn×16 => u128/i128

(little-endian, i128 is two's complement)

| 0xnn×32 => u256

| b:0xnn => Boolean

(false if b==0, true otherwise)

| 0xnn×21 => Address

| 0xnn×32 => Hash

| 0xnn×33 => PublicKey

| 0xnn×65 => Signature

| 0xnn×96 => BlsPublicKey

| 0xnn×48 => BlsSignature

| elems:T×len => Array[T;len]

(containing the len values)

| len:LengthState utf8:0xnn×len => String

(with len UTF-8 encoded bytes)

| len:LengthState elems:T×len => Vec<T>

(containing the len elements)

| b:0x00 => Option::None

| b:0x01 arg:T => Option::Some<T>

| f1:State ... fn:State => Struct S {f1,f2,...,fn}

| variant:0xnn f1:State ... fn:State => Enum{variant,f1,f2,...,fn}

| 0xnn×4 => AvlTree{f1,f2,...,fn}

Only arrays of lengths between (including) 0 and 127 are supported. The high bit in length is reserved for later extensions.

For arguments with variable lengths, such as Vecs or Strings the number of elements is represented as a little-endian 32-bit unsigned integer.

LengthState

::= 0xnn×4 => u32

(little-endian)

Note: An AvlTreeMap is only stored in wasm state as an integer. The actual content of the AvlTreeMap is stored outside the wasm code. The content of the AvlTreeMap is accessed using external calls to the java code. The integer stored is the tree id of the AvlTreeMap and is used as a pointer to find the correct AvlTreeMap. This is done to avoid having to serialize and deserialize the entire content of the Map for every invocation saving gas cost.

CopySerializable Binary Format

A state type is said to be CopySerializable, if it's serialization is identical to its in-memory representation, and thus requires minimal serialization overhead. The Partisia Platform have efficient handling for types that are CopySerializable. Internal pointers are the main reason that types are not CopySerializable.

CopySerializable

::=

uXXX => true

| iXXX => true

| Boolean => true

| Address => true

| [T;n] => CopySerializable(T) ∧ WellAligned([T;n])

| String => false

| Vec<T> => false

| Option<T> => false

| SortedVecMap<K,V> => false

| SortedVecSet<K,V> => false

| AvlTreeMap<K,V> => false

| Struct S {f1:T1,...,fn:Tn} => CopySerializable(T1) ∧ ... ∧ CopySerializable(Tn) ∧ WellAligned(S)

| Enum{variant, f1, f2,...,fn} => false

The WellAligned constraint on Struct CopySerializable is to guarantee that struct layouts are identical to serialization.

Struct S {f1:T1,...,fn:T n}

is WellAligned if following points hold:

  1. Annotated with #[repr(C)]. Read the Rust Specification for details on this representation.
  2. No padding:

    size_of(S) = size_of(T1) + ... + size_of(T n).

  3. No wasted bytes when stored in array:

    size_of(S) mod align_of(Tn) = 0.

It may be desirable to manually add "padding" fields structs in order to achieve CopySerializable. While this will use extra unneeded bytes for the serialized state, it may significantly improve serialization speed and lower gas costs. A future version of the compiler may automatically add padding fields.

Examples

These structs are CopySerializable:

  • Struct E1 { /* empty */ }
  • Struct E2 { f1: u32 }
  • Struct E3 { f1: u32, f2: u32 }

These structs are not CopySerializable:

  • Struct E4 { f1: u8, f2: u16 } due to padding between f1 and f2.
  • Struct E5 { f1: u16, f2: u8 } due to alignment not dividing size.
  • Struct E6 { f1: Vec<u8> } due to non-CopySerializable subfield.

Partisia All Rights Reserved © 2023