Skip to content

Commit

Permalink
Fallible storage get and set methods for Lazy and Mapping (#1910
Browse files Browse the repository at this point in the history
)

Add `try_*` methods for reading and writing Lazy and Mapping values to and from storage. For `Mapping`, the encoded size of the key is also accounted for.

Fallible writes are implemented by extending the `Storable` trait with `encoded_size` method (which provides a default implementation using the SCALE version method of `encoded_size`). There is also `size_hint` in SCALE but we want an exact number and not an estimate. This will encode the stored value twice (the first time it's thrown away), however users can optimize it by overwriting the `encoded_size` method to return a constant instead if possible and required.

Fallible reads are implemented using the `contains_storage_key` API, which will tell the size. This implies two calls to the contracts pallet (one to get the size and one to read the value). If required, this can be improved by adding a corresponding `ReturnCode` to the contracts pallet, which can then be used by the `get_storage` API method.
  • Loading branch information
xermicus authored Oct 27, 2023
1 parent 9cf5b3d commit 48ed3f8
Show file tree
Hide file tree
Showing 9 changed files with 504 additions and 2 deletions.
2 changes: 2 additions & 0 deletions crates/env/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use crate::engine::off_chain::OffChainError;
pub enum Error {
/// Error upon decoding an encoded value.
Decode(scale::Error),
/// The static buffer used during ABI encoding or ABI decoding is too small.
BufferTooSmall,
/// An error that can only occur in the off-chain environment.
#[cfg(any(feature = "std", test, doc))]
OffChain(OffChainError),
Expand Down
37 changes: 37 additions & 0 deletions crates/ink/macro/src/storage/storable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 {
::ink::storage::traits::Storable::encode(#binding, __dest);
)
});
let encoded_size_body =
variant.fold(quote!(::core::primitive::usize::MIN), |acc, binding| {
let span = binding.ast().ty.span();
quote_spanned!(span =>
#acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#binding))
)
});

s.gen_impl(quote! {
gen impl ::ink::storage::traits::Storable for @Self {
Expand All @@ -50,6 +57,12 @@ fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 {
fn encode<__ink_O: ::ink::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) {
match self { #encode_body }
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self { #encoded_size_body }
}
}
})
}
Expand Down Expand Up @@ -108,6 +121,20 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 {
}
}
});

let encoded_size_body = s.variants().iter().map(|variant| {
let pat = variant.pat();
let field = variant.bindings().iter().fold(quote!(1usize), |acc, field| {
let span = field.ast().ty.span();
quote_spanned!(span =>
#acc.saturating_add(::ink::storage::traits::Storable::encoded_size(#field))
)
});
quote! {
#pat => { #field }
}
});

s.gen_impl(quote! {
gen impl ::ink::storage::traits::Storable for @Self {
#[inline(always)]
Expand All @@ -130,6 +157,16 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 {
)*
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
#(
#encoded_size_body
)*
}
}
}
})
}
Expand Down
76 changes: 76 additions & 0 deletions crates/ink/macro/src/tests/storable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ fn unit_struct_works() {
UnitStruct => { }
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
UnitStruct => { ::core::primitive::usize::MIN }
}
}
}
};
}
Expand Down Expand Up @@ -106,6 +114,19 @@ fn struct_works() {
}
}
}

#[inline (always)]
#[allow (non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
NamedFields { a : __binding_0 , b : __binding_1 , d : __binding_2 , } => {
::core::primitive::usize::MIN
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_2))
}
}
}
}
};
}
Expand Down Expand Up @@ -149,6 +170,14 @@ fn one_variant_enum_works() {
}
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size (&self) -> ::core::primitive::usize {
match self {
OneVariantEnum::A => { 1usize }
}
}
}
};
}
Expand Down Expand Up @@ -244,6 +273,24 @@ fn enum_works() {
}
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size (&self) -> ::core::primitive::usize{
match self {
MixedEnum::A => { 1usize }
MixedEnum::B(__binding_0, __binding_1,) => {
1usize
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
}
MixedEnum::C { a: __binding_0, b: __binding_1, } => {
1usize
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
}
}
}
}
};
}
Expand Down Expand Up @@ -310,6 +357,18 @@ fn generic_struct_works() {
}
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
GenericStruct { a : __binding_0 , b : __binding_1 , } => {
::core::primitive::usize::MIN
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
}
}
}
}
};
}
Expand Down Expand Up @@ -394,6 +453,23 @@ fn generic_enum_works() {
}
}
}

#[inline(always)]
#[allow(non_camel_case_types)]
fn encoded_size(&self) -> ::core::primitive::usize {
match self {
GenericEnum::Tuple (__binding_0, __binding_1,) => {
1usize
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
}
GenericEnum::Named { a: __binding_0, b: __binding_1,} => {
1usize
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_0))
.saturating_add(::ink::storage::traits::Storable::encoded_size(__binding_1))
}
}
}
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ impl<KEY: StorageKey> Storable for Contract<KEY> {
c: Default::default(),
})
}

fn encoded_size(&self) -> usize {
2 + 8 + 16
}
}

fn main() {
Expand Down
Loading

0 comments on commit 48ed3f8

Please sign in to comment.