From 3d969bf22125873c9a7210b5dd68514324008899 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 20 Jul 2022 19:39:39 +0100 Subject: [PATCH] Test commit --- .config/cargo_spellcheck.dic | 1 + crates/engine/src/ext.rs | 12 +- crates/env/src/api.rs | 36 +- crates/env/src/backend.rs | 26 +- crates/env/src/engine/off_chain/impls.rs | 25 +- crates/env/src/engine/on_chain/buffer.rs | 6 +- crates/env/src/engine/on_chain/ext.rs | 89 ++- crates/env/src/engine/on_chain/impls.rs | 49 +- crates/env/src/engine/on_chain/mod.rs | 1 + crates/lang/codegen/Cargo.toml | 1 + .../generator/as_dependency/call_builder.rs | 2 - .../generator/as_dependency/contract_ref.rs | 2 - crates/lang/codegen/src/generator/dispatch.rs | 12 +- crates/lang/codegen/src/generator/metadata.rs | 25 +- crates/lang/codegen/src/generator/mod.rs | 2 + crates/lang/codegen/src/generator/selector.rs | 2 +- crates/lang/codegen/src/generator/storage.rs | 13 +- .../codegen/src/generator/storage_item.rs | 248 ++++++ .../src/generator/trait_def/call_builder.rs | 98 +-- .../src/generator/trait_def/call_forwarder.rs | 81 +- crates/lang/codegen/src/lib.rs | 4 + crates/lang/ir/Cargo.toml | 1 + crates/lang/ir/src/ir/item/storage.rs | 24 +- crates/lang/ir/src/ir/mod.rs | 2 + crates/lang/ir/src/ir/storage_item/config.rs | 85 ++ crates/lang/ir/src/ir/storage_item/mod.rs | 89 +++ crates/lang/ir/src/lib.rs | 1 + crates/lang/macro/Cargo.toml | 1 + crates/lang/macro/src/lib.rs | 145 +++- .../macro/src/storage_item.rs} | 19 + crates/lang/src/codegen/dispatch/execution.rs | 57 +- crates/lang/src/codegen/dispatch/mod.rs | 2 - crates/lang/src/codegen/mod.rs | 2 - crates/lang/src/lib.rs | 8 +- crates/lang/tests/compile_tests.rs | 3 + .../fail/message-returns-non-codec.stderr | 2 +- .../ui/contract/pass/example-erc20-works.rs | 13 +- .../ui/contract/pass/example-erc721-works.rs | 11 +- .../ui/contract/pass/storage-packed-fields.rs | 17 +- .../fail/argument_derive_invalid_type.rs | 9 + .../fail/argument_derive_invalid_type.stderr | 5 + .../fail/argument_derive_missing_arg.rs | 9 + .../fail/argument_derive_missing_arg.stderr | 5 + .../fail/collections_only_packed_1.rs | 14 + .../fail/collections_only_packed_1.stderr | 118 +++ .../fail/collections_only_packed_2.rs | 14 + .../fail/collections_only_packed_2.stderr | 109 +++ .../packed_is_not_derived_automatically.rs | 16 + ...packed_is_not_derived_automatically.stderr | 41 + .../pass/argument_derive_false.rs | 30 + .../pass/complex_non_packed_enum.rs | 154 ++++ .../pass/complex_non_packed_struct.rs | 88 +++ .../storage_item/pass/complex_packed_enum.rs | 59 ++ .../pass/complex_packed_struct.rs | 50 ++ .../pass/default_storage_key_1.rs | 15 + .../pass/default_storage_key_2.rs | 15 + .../pass/default_storage_key_3.rs | 12 + .../pass/default_storage_key_4.rs | 12 + .../pass/non_packed_tuple_struct.rs | 22 + .../storage_item/pass/packed_tuple_struct.rs | 12 + .../fail/message_output_non_codec.stderr | 2 +- crates/metadata/src/layout/mod.rs | 190 ++++- crates/metadata/src/layout/tests.rs | 249 +++--- crates/metadata/src/layout/validate.rs | 298 +++++++ crates/primitives/Cargo.toml | 12 +- crates/primitives/benches/bench.rs | 121 --- crates/primitives/src/key.rs | 382 ++------- crates/primitives/src/key_ptr.rs | 49 -- crates/primitives/src/lib.rs | 10 +- crates/primitives/src/tests.rs | 294 ------- crates/storage/Cargo.toml | 1 + crates/storage/codegen/Cargo.toml | 19 + crates/storage/codegen/LICENSE | 1 + crates/storage/codegen/README.md | 1 + crates/storage/codegen/src/lib.rs | 80 ++ crates/storage/derive/Cargo.toml | 3 +- crates/storage/derive/src/item.rs | 87 ++ crates/storage/derive/src/key_holder.rs | 37 + crates/storage/derive/src/lib.rs | 183 ++--- crates/storage/derive/src/packed_layout.rs | 45 -- crates/storage/derive/src/spread_allocate.rs | 51 -- crates/storage/derive/src/spread_layout.rs | 215 ----- crates/storage/derive/src/storable.rs | 140 ++++ crates/storage/derive/src/storage_layout.rs | 44 +- crates/storage/derive/src/tests/item.rs | 160 ++++ crates/storage/derive/src/tests/key_holder.rs | 192 +++++ crates/storage/derive/src/tests/mod.rs | 38 +- .../storage/derive/src/tests/packed_layout.rs | 377 --------- .../derive/src/tests/spread_allocate.rs | 108 --- .../storage/derive/src/tests/spread_layout.rs | 744 ------------------ crates/storage/derive/src/tests/storable.rs | 401 ++++++++++ .../derive/src/tests/storage_layout.rs | 182 +++-- crates/storage/src/lazy/mapping.rs | 220 +++--- crates/storage/src/lazy/mod.rs | 247 +++++- crates/storage/src/lib.rs | 16 +- crates/storage/src/pack.rs | 555 ------------- crates/storage/src/test_utils.rs | 46 +- crates/storage/src/traits/impls/arrays.rs | 106 +-- .../storage/src/traits/impls/collections.rs | 288 ------- crates/storage/src/traits/impls/fuzz_tests.rs | 89 --- crates/storage/src/traits/impls/mod.rs | 166 +--- crates/storage/src/traits/impls/prims.rs | 337 +------- crates/storage/src/traits/impls/storage.rs | 107 +++ crates/storage/src/traits/impls/tuples.rs | 160 +--- crates/storage/src/traits/keyptr.rs | 35 - crates/storage/src/traits/layout/impls.rs | 203 +++-- crates/storage/src/traits/layout/mod.rs | 9 +- crates/storage/src/traits/mod.rs | 223 ++---- crates/storage/src/traits/optspec.rs | 41 - crates/storage/src/traits/packed.rs | 53 -- crates/storage/src/traits/pull_or_init.rs | 111 +++ crates/storage/src/traits/spread.rs | 104 --- crates/storage/src/traits/storage.rs | 97 +++ examples/delegator/lib.rs | 18 +- examples/dns/lib.rs | 15 +- examples/erc1155/lib.rs | 13 +- examples/erc20/lib.rs | 22 +- examples/erc721/lib.rs | 11 +- examples/mother/Cargo.toml | 2 +- examples/mother/lib.rs | 87 +- examples/multisig/lib.rs | 41 +- examples/trait-erc20/lib.rs | 13 +- .../delegate-calls/lib.rs | 79 +- .../delegate-calls/upgradeable-flipper/lib.rs | 42 +- .../upgradeable-flipper/upgradeable.rs | 178 ----- .../updated-incrementer/Cargo.toml | 2 +- 126 files changed, 4592 insertions(+), 5886 deletions(-) create mode 100644 crates/lang/codegen/src/generator/storage_item.rs create mode 100644 crates/lang/ir/src/ir/storage_item/config.rs create mode 100644 crates/lang/ir/src/ir/storage_item/mod.rs rename crates/{storage/src/traits/layout/tests.rs => lang/macro/src/storage_item.rs} (54%) create mode 100644 crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.stderr create mode 100644 crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.stderr create mode 100644 crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.stderr create mode 100644 crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.stderr create mode 100644 crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.stderr create mode 100644 crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/complex_non_packed_enum.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/complex_non_packed_struct.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/default_storage_key_1.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/default_storage_key_2.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/default_storage_key_3.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/default_storage_key_4.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/non_packed_tuple_struct.rs create mode 100644 crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs create mode 100644 crates/metadata/src/layout/validate.rs delete mode 100644 crates/primitives/benches/bench.rs delete mode 100644 crates/primitives/src/key_ptr.rs delete mode 100644 crates/primitives/src/tests.rs create mode 100644 crates/storage/codegen/Cargo.toml create mode 120000 crates/storage/codegen/LICENSE create mode 120000 crates/storage/codegen/README.md create mode 100644 crates/storage/codegen/src/lib.rs create mode 100644 crates/storage/derive/src/item.rs create mode 100644 crates/storage/derive/src/key_holder.rs delete mode 100644 crates/storage/derive/src/packed_layout.rs delete mode 100644 crates/storage/derive/src/spread_allocate.rs delete mode 100644 crates/storage/derive/src/spread_layout.rs create mode 100644 crates/storage/derive/src/storable.rs create mode 100644 crates/storage/derive/src/tests/item.rs create mode 100644 crates/storage/derive/src/tests/key_holder.rs delete mode 100644 crates/storage/derive/src/tests/packed_layout.rs delete mode 100644 crates/storage/derive/src/tests/spread_allocate.rs delete mode 100644 crates/storage/derive/src/tests/spread_layout.rs create mode 100644 crates/storage/derive/src/tests/storable.rs delete mode 100644 crates/storage/src/pack.rs delete mode 100644 crates/storage/src/traits/impls/collections.rs delete mode 100644 crates/storage/src/traits/impls/fuzz_tests.rs create mode 100644 crates/storage/src/traits/impls/storage.rs delete mode 100644 crates/storage/src/traits/keyptr.rs delete mode 100644 crates/storage/src/traits/optspec.rs delete mode 100644 crates/storage/src/traits/packed.rs create mode 100644 crates/storage/src/traits/pull_or_init.rs delete mode 100644 crates/storage/src/traits/spread.rs create mode 100644 crates/storage/src/traits/storage.rs delete mode 100644 examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/upgradeable.rs diff --git a/.config/cargo_spellcheck.dic b/.config/cargo_spellcheck.dic index 6a430c8bc3c..4fd73ced651 100644 --- a/.config/cargo_spellcheck.dic +++ b/.config/cargo_spellcheck.dic @@ -38,6 +38,7 @@ defragmentation deploy dereferencing deserialize/S +deserialization dispatchable/S encodable evaluable diff --git a/crates/engine/src/ext.rs b/crates/engine/src/ext.rs index d784d12094b..e5ae5f488fa 100644 --- a/crates/engine/src/ext.rs +++ b/crates/engine/src/ext.rs @@ -229,7 +229,7 @@ impl Engine { /// Writes the encoded value into the storage at the given key. /// Returns the size of the previously stored value at the key if any. - pub fn set_storage(&mut self, key: &[u8; 32], encoded_value: &[u8]) -> Option { + pub fn set_storage(&mut self, key: &[u8], encoded_value: &[u8]) -> Option { let callee = self.get_callee(); let account_id = AccountId::from_bytes(&callee[..]); @@ -243,7 +243,7 @@ impl Engine { } /// Returns the decoded contract storage at the key if any. - pub fn get_storage(&mut self, key: &[u8; 32], output: &mut &mut [u8]) -> Result { + pub fn get_storage(&mut self, key: &[u8], output: &mut &mut [u8]) -> Result { let callee = self.get_callee(); let account_id = AccountId::from_bytes(&callee[..]); @@ -258,7 +258,7 @@ impl Engine { } /// Returns the size of the value stored in the contract storage at the key if any. - pub fn contains_storage(&mut self, key: &[u8; 32]) -> Option { + pub fn contains_storage(&mut self, key: &[u8]) -> Option { let callee = self.get_callee(); let account_id = AccountId::from_bytes(&callee[..]); @@ -269,14 +269,16 @@ impl Engine { } /// Removes the storage entries at the given key. - pub fn clear_storage(&mut self, key: &[u8; 32]) { + pub fn clear_storage(&mut self, key: &[u8]) -> Option { let callee = self.get_callee(); let account_id = AccountId::from_bytes(&callee[..]); self.debug_info.inc_writes(account_id.clone()); let _ = self .debug_info .remove_cell_for_account(account_id, key.to_vec()); - let _ = self.database.remove_contract_storage(&callee, key); + self.database + .remove_contract_storage(&callee, key) + .map(|val| val.len() as u32) } /// Remove the calling account and transfer remaining balance. diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 581d7dabb4f..8796eac9ba0 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -39,7 +39,6 @@ use crate::{ Environment, Result, }; -use ink_primitives::Key; /// Returns the address of the caller of the executed contract. /// @@ -183,49 +182,56 @@ where }) } -/// Writes the value to the contract storage under the given key and returns -/// the size of pre-existing value at the specified key if any. +/// Writes the value to the contract storage under the given storage key and returns the size +/// of pre-existing value if any. /// /// # Panics /// /// - If the encode length of value exceeds the configured maximum value length of a storage entry. -pub fn set_contract_storage(key: &Key, value: &V) -> Option +pub fn set_contract_storage(key: &K, value: &V) -> Option where + K: scale::Encode, V: scale::Encode, { ::on_instance(|instance| { - EnvBackend::set_contract_storage::(instance, key, value) + EnvBackend::set_contract_storage::(instance, key, value) }) } -/// Returns the value stored under the given key in the contract's storage if any. +/// Returns the value stored under the given storage key in the contract's storage if any. /// /// # Errors /// /// - If the decoding of the typed value failed (`KeyNotFound`) -pub fn get_contract_storage(key: &Key) -> Result> +pub fn get_contract_storage(key: &K) -> Result> where + K: scale::Encode, R: scale::Decode, { ::on_instance(|instance| { - EnvBackend::get_contract_storage::(instance, key) + EnvBackend::get_contract_storage::(instance, key) }) } -/// Checks whether there is a value stored under the given key in -/// the contract's storage. +/// Checks whether there is a value stored under the given storage key in the contract's storage. /// /// If a value is stored under the specified key, the size of the value is returned. -pub fn contract_storage_contains(key: &Key) -> Option { +pub fn contains_contract_storage(key: &K) -> Option +where + K: scale::Encode, +{ ::on_instance(|instance| { - EnvBackend::contract_storage_contains(instance, key) + EnvBackend::contains_contract_storage::(instance, key) }) } -/// Clears the contract's storage key entry. -pub fn clear_contract_storage(key: &Key) { +/// Clears the contract's storage entry under the given storage key. +pub fn clear_contract_storage(key: &K) -> Option +where + K: scale::Encode, +{ ::on_instance(|instance| { - EnvBackend::clear_contract_storage(instance, key) + EnvBackend::clear_contract_storage::(instance, key) }) } diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 43ee3252332..edb1bb52243 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -27,7 +27,6 @@ use crate::{ Environment, Result, }; -use ink_primitives::Key; /// The flags to indicate further information about the end of a contract execution. #[derive(Default)] @@ -162,26 +161,33 @@ impl CallFlags { /// Environmental contract functionality that does not require `Environment`. pub trait EnvBackend { - /// Writes the value to the contract storage under the given key and returns - /// the size of the pre-existing value at the specified key if any. - fn set_contract_storage(&mut self, key: &Key, value: &V) -> Option + /// Writes the value to the contract storage under the given storage key. + /// + /// Returns the size of the pre-existing value at the specified key if any. + fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where + K: scale::Encode, V: scale::Encode; - /// Returns the value stored under the given key in the contract's storage if any. + /// Returns the value stored under the given storage key in the contract's storage if any. /// /// # Errors /// /// - If the decoding of the typed value failed - fn get_contract_storage(&mut self, key: &Key) -> Result> + fn get_contract_storage(&mut self, key: &K) -> Result> where + K: scale::Encode, R: scale::Decode; - /// Returns the size of a value stored under the specified key is returned if any. - fn contract_storage_contains(&mut self, key: &Key) -> Option; + /// Returns the size of a value stored under the given storage key is returned if any. + fn contains_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode; - /// Clears the contract's storage key entry. - fn clear_contract_storage(&mut self, key: &Key); + /// Clears the contract's storage key entry under the given storage key. + fn clear_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode; /// Returns the execution input to the executed contract and decodes it as `T`. /// diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 26e1e3b4f1a..9037014dfcf 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -44,7 +44,6 @@ use ink_engine::{ ext, ext::Engine, }; -use ink_primitives::Key; /// The capacity of the static buffer. /// This is the same size as the ink! on-chain environment. We chose to use the same size @@ -184,20 +183,22 @@ impl EnvInstance { } impl EnvBackend for EnvInstance { - fn set_contract_storage(&mut self, key: &Key, value: &V) -> Option + fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where + K: scale::Encode, V: scale::Encode, { let v = scale::Encode::encode(value); - self.engine.set_storage(key.as_ref(), &v[..]) + self.engine.set_storage(&key.encode(), &v[..]) } - fn get_contract_storage(&mut self, key: &Key) -> Result> + fn get_contract_storage(&mut self, key: &K) -> Result> where + K: scale::Encode, R: scale::Decode, { let mut output: [u8; 9600] = [0; 9600]; - match self.engine.get_storage(key.as_ref(), &mut &mut output[..]) { + match self.engine.get_storage(&key.encode(), &mut &mut output[..]) { Ok(_) => (), Err(ext::Error::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), @@ -206,12 +207,18 @@ impl EnvBackend for EnvInstance { Ok(Some(decoded)) } - fn contract_storage_contains(&mut self, key: &Key) -> Option { - self.engine.contains_storage(key.as_ref()) + fn contains_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode, + { + self.engine.contains_storage(&key.encode()) } - fn clear_contract_storage(&mut self, key: &Key) { - self.engine.clear_storage(key.as_ref()) + fn clear_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode, + { + self.engine.clear_storage(&key.encode()) } fn decode_input(&mut self) -> Result diff --git a/crates/env/src/engine/on_chain/buffer.rs b/crates/env/src/engine/on_chain/buffer.rs index 469238f2524..29bdb72f198 100644 --- a/crates/env/src/engine/on_chain/buffer.rs +++ b/crates/env/src/engine/on_chain/buffer.rs @@ -33,12 +33,14 @@ impl StaticBuffer { impl core::ops::Index for StaticBuffer { type Output = [u8]; + #[inline(always)] fn index(&self, index: core::ops::RangeFull) -> &Self::Output { core::ops::Index::index(&self.buffer[..], index) } } impl core::ops::IndexMut for StaticBuffer { + #[inline(always)] fn index_mut(&mut self, index: core::ops::RangeFull) -> &mut Self::Output { core::ops::IndexMut::index_mut(&mut self.buffer[..], index) } @@ -47,7 +49,7 @@ impl core::ops::IndexMut for StaticBuffer { /// Utility to allow for non-heap allocating encoding into a static buffer. /// /// Required by `ScopedBuffer` internals. -struct EncodeScope<'a> { +pub struct EncodeScope<'a> { buffer: &'a mut [u8], len: usize, } @@ -154,6 +156,7 @@ impl<'a> ScopedBuffer<'a> { /// Encode the given value into the scoped buffer and return the sub slice /// containing all the encoded bytes. + #[inline(always)] pub fn take_encoded(&mut self, value: &T) -> &'a mut [u8] where T: scale::Encode, @@ -172,6 +175,7 @@ impl<'a> ScopedBuffer<'a> { /// Does not return the buffer immediately so that other values can be appended /// afterwards. The [`take_appended`] method shall be used to return the buffer /// that includes all appended encodings as a single buffer. + #[inline(always)] pub fn append_encoded(&mut self, value: &T) where T: scale::Encode, diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs index 0ab029e22c3..cb80d3449e5 100644 --- a/crates/env/src/engine/on_chain/ext.rs +++ b/crates/env/src/engine/on_chain/ext.rs @@ -231,16 +231,6 @@ mod sys { data_len: u32, ); - pub fn seal_get_storage( - key_ptr: Ptr32<[u8]>, - output_ptr: Ptr32Mut<[u8]>, - output_len_ptr: Ptr32Mut, - ) -> ReturnCode; - - pub fn seal_contains_storage(key_ptr: Ptr32<[u8]>) -> ReturnCode; - - pub fn seal_clear_storage(key_ptr: Ptr32<[u8]>); - pub fn seal_call_chain_extension( func_id: u32, input_ptr: Ptr32<[u8]>, @@ -374,21 +364,78 @@ mod sys { output_ptr: Ptr32Mut<[u8]>, output_len_ptr: Ptr32Mut, ) -> ReturnCode; + } + #[link(wasm_import_module = "__unstable__")] + extern "C" { + // # Parameters + // + // - `key_ptr`: pointer into the linear memory where the location to store the value is placed. + // - `key_len`: the length of the key in bytes. + // - `value_ptr`: pointer into the linear memory where the value to set is placed. + // - `value_len`: the length of the value in bytes. + // + // # Return Value + // + // Returns the size of the pre-existing value at the specified key if any. Otherwise + // `SENTINEL` is returned as a sentinel value. pub fn seal_set_storage( key_ptr: Ptr32<[u8]>, + key_len: u32, value_ptr: Ptr32<[u8]>, value_len: u32, ) -> ReturnCode; + + // # Parameters + // + // - `key_ptr`: pointer into the linear memory where the key is placed. + // - `key_len`: the length of the key in bytes. + // + // # Return Value + // + // Returns the size of the pre-existing value at the specified key if any. Otherwise + // `SENTINEL` is returned as a sentinel value. + pub fn seal_clear_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode; + + // # Parameters + // + // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + // - `key_len`: the length of the key in bytes. + // + // # Return Value + // + // Returns the size of the pre-existing value at the specified key if any. Otherwise + // `SENTINEL` is returned as a sentinel value. + pub fn seal_contains_storage(key_ptr: Ptr32<[u8]>, key_len: u32) -> ReturnCode; + + // # Parameters + // + // - `key_ptr`: pointer into the linear memory where the key of the requested value is placed. + // - `key_len`: the length of the key in bytes. + // - `out_ptr`: pointer to the linear memory where the value is written to. + // - `out_len_ptr`: in-out pointer into linear memory where the buffer length + // is read from and the value length is written to. + // + // # Errors + // + // `ReturnCode::KeyNotFound` + pub fn seal_get_storage( + key_ptr: Ptr32<[u8]>, + key_len: u32, + out_ptr: Ptr32Mut<[u8]>, + out_len_ptr: Ptr32Mut, + ) -> ReturnCode; } } +#[inline(always)] fn extract_from_slice(output: &mut &mut [u8], new_len: usize) { debug_assert!(new_len <= output.len()); let tmp = core::mem::take(output); *output = &mut tmp[..new_len]; } +#[inline(always)] pub fn instantiate( code_hash: &[u8], gas_limit: u64, @@ -422,6 +469,7 @@ pub fn instantiate( ret_code.into() } +#[inline(always)] pub fn call( flags: u32, callee: &[u8], @@ -449,6 +497,7 @@ pub fn call( ret_code.into() } +#[inline(always)] pub fn delegate_call( flags: u32, code_hash: &[u8], @@ -499,6 +548,7 @@ pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { let ret_code = unsafe { sys::seal_set_storage( Ptr32::from_slice(key), + key.len() as u32, Ptr32::from_slice(encoded_value), encoded_value.len() as u32, ) @@ -506,16 +556,20 @@ pub fn set_storage(key: &[u8], encoded_value: &[u8]) -> Option { ret_code.into() } -pub fn clear_storage(key: &[u8]) { - unsafe { sys::seal_clear_storage(Ptr32::from_slice(key)) } +pub fn clear_storage(key: &[u8]) -> Option { + let ret_code = + unsafe { sys::seal_clear_storage(Ptr32::from_slice(key), key.len() as u32) }; + ret_code.into() } +#[inline(always)] pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { let mut output_len = output.len() as u32; let ret_code = { unsafe { sys::seal_get_storage( Ptr32::from_slice(key), + key.len() as u32, Ptr32Mut::from_slice(output), Ptr32Mut::from_ref(&mut output_len), ) @@ -526,7 +580,8 @@ pub fn get_storage(key: &[u8], output: &mut &mut [u8]) -> Result { } pub fn storage_contains(key: &[u8]) -> Option { - let ret_code = unsafe { sys::seal_contains_storage(Ptr32::from_slice(key)) }; + let ret_code = + unsafe { sys::seal_contains_storage(Ptr32::from_slice(key), key.len() as u32) }; ret_code.into() } @@ -534,6 +589,7 @@ pub fn terminate(beneficiary: &[u8]) -> ! { unsafe { sys::seal_terminate(Ptr32::from_slice(beneficiary)) } } +#[inline(always)] pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) -> u32 { let mut output_len = output.len() as u32; let ret_code = { @@ -551,6 +607,7 @@ pub fn call_chain_extension(func_id: u32, input: &[u8], output: &mut &mut [u8]) ret_code.into_u32() } +#[inline(always)] pub fn input(output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { @@ -577,6 +634,7 @@ pub fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { macro_rules! impl_seal_wrapper_for { ( $( ($name:ident => $seal_name:ident), )* ) => { $( + #[inline(always)] pub fn $name(output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { @@ -587,7 +645,8 @@ macro_rules! impl_seal_wrapper_for { ) }; } - extract_from_slice(output, output_len as usize); + // We don't need `extract_from_slice` here because it is stored in the own array + // in `get_property_little_endian` and `get_property_inplace` } )* } @@ -603,6 +662,7 @@ impl_seal_wrapper_for! { (minimum_balance => seal_minimum_balance), } +#[inline(always)] pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { @@ -617,6 +677,7 @@ pub fn weight_to_fee(gas: u64, output: &mut &mut [u8]) { extract_from_slice(output, output_len as usize); } +#[inline(always)] pub fn random(subject: &[u8], output: &mut &mut [u8]) { let mut output_len = output.len() as u32; { diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index e8a98d71c6f..883b0fadd29 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -14,6 +14,7 @@ use super::{ ext, + EncodeScope, EnvInstance, Error as ExtError, ScopedBuffer, @@ -46,7 +47,6 @@ use crate::{ ReturnFlags, TypedEnvBackend, }; -use ink_primitives::Key; impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { @@ -174,6 +174,7 @@ where } impl EnvInstance { + #[inline(always)] /// Returns a new scoped buffer for the entire scope of the static 16 kB buffer. fn scoped_buffer(&mut self) -> ScopedBuffer { ScopedBuffer::from(&mut self.buffer[..]) @@ -184,6 +185,7 @@ impl EnvInstance { /// # Note /// /// This skips the potentially costly decoding step that is often equivalent to a `memcpy`. + #[inline(always)] fn get_property_inplace(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> T where T: Default + AsMut<[u8]>, @@ -198,6 +200,7 @@ impl EnvInstance { /// # Note /// /// This skips the potentially costly decoding step that is often equivalent to a `memcpy`. + #[inline(always)] fn get_property_little_endian(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> T where T: FromLittleEndian, @@ -208,6 +211,7 @@ impl EnvInstance { } /// Returns the contract property value. + #[inline(always)] fn get_property(&mut self, ext_fn: fn(output: &mut &mut [u8])) -> Result where T: scale::Decode, @@ -219,20 +223,26 @@ impl EnvInstance { } impl EnvBackend for EnvInstance { - fn set_contract_storage(&mut self, key: &Key, value: &V) -> Option + fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where + K: scale::Encode, V: scale::Encode, { - let buffer = self.scoped_buffer().take_encoded(value); - ext::set_storage(key.as_ref(), buffer) + let mut buffer = self.scoped_buffer(); + let key = buffer.take_encoded(key); + let value = buffer.take_encoded(value); + ext::set_storage(key, value) } - fn get_contract_storage(&mut self, key: &Key) -> Result> + fn get_contract_storage(&mut self, key: &K) -> Result> where + K: scale::Encode, R: scale::Decode, { - let output = &mut self.scoped_buffer().take_rest(); - match ext::get_storage(key.as_ref(), output) { + let mut buffer = self.scoped_buffer(); + let key = buffer.take_encoded(key); + let output = &mut buffer.take_rest(); + match ext::get_storage(key, output) { Ok(_) => (), Err(ExtError::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), @@ -241,12 +251,22 @@ impl EnvBackend for EnvInstance { Ok(Some(decoded)) } - fn contract_storage_contains(&mut self, key: &Key) -> Option { - ext::storage_contains(key.as_ref()) + fn contains_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode, + { + let mut buffer = self.scoped_buffer(); + let key = buffer.take_encoded(key); + ext::storage_contains(key) } - fn clear_contract_storage(&mut self, key: &Key) { - ext::clear_storage(key.as_ref()) + fn clear_contract_storage(&mut self, key: &K) -> Option + where + K: scale::Encode, + { + let mut buffer = self.scoped_buffer(); + let key = buffer.take_encoded(key); + ext::clear_storage(key) } fn decode_input(&mut self) -> Result @@ -260,9 +280,10 @@ impl EnvBackend for EnvInstance { where R: scale::Encode, { - let mut scope = self.scoped_buffer(); - let enc_return_value = scope.take_encoded(return_value); - ext::return_value(flags, enc_return_value); + let mut scope = EncodeScope::from(&mut self.buffer[..]); + return_value.encode_to(&mut scope); + let len = scope.len(); + ext::return_value(flags, &self.buffer[..][..len]); } fn debug_message(&mut self, content: &str) { diff --git a/crates/env/src/engine/on_chain/mod.rs b/crates/env/src/engine/on_chain/mod.rs index 2bee4c0785b..8969748b773 100644 --- a/crates/env/src/engine/on_chain/mod.rs +++ b/crates/env/src/engine/on_chain/mod.rs @@ -18,6 +18,7 @@ mod impls; use self::{ buffer::{ + EncodeScope, ScopedBuffer, StaticBuffer, }, diff --git a/crates/lang/codegen/Cargo.toml b/crates/lang/codegen/Cargo.toml index dc8de56401c..7b46282a72f 100644 --- a/crates/lang/codegen/Cargo.toml +++ b/crates/lang/codegen/Cargo.toml @@ -18,6 +18,7 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] name = "ink_lang_codegen" [dependencies] +ink_primitives = { version = "4.0.0", path = "../../primitives" } ir = { version = "4.0.0", package = "ink_lang_ir", path = "../ir", default-features = false } quote = "1" syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] } diff --git a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs index 074b7c66843..ca596d5628f 100644 --- a/crates/lang/codegen/src/generator/as_dependency/call_builder.rs +++ b/crates/lang/codegen/src/generator/as_dependency/call_builder.rs @@ -88,8 +88,6 @@ impl CallBuilder<'_> { ))] #[derive( ::core::fmt::Debug, - ::ink_storage::traits::SpreadLayout, - ::ink_storage::traits::PackedLayout, ::scale::Encode, ::scale::Decode, ::core::hash::Hash, diff --git a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs index 77bb46940e0..7357da1b4f2 100644 --- a/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs +++ b/crates/lang/codegen/src/generator/as_dependency/contract_ref.rs @@ -92,8 +92,6 @@ impl ContractRef<'_> { ))] #[derive( ::core::fmt::Debug, - ::ink_storage::traits::SpreadLayout, - ::ink_storage::traits::PackedLayout, ::scale::Encode, ::scale::Decode, ::core::hash::Hash, diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 6ea44b7a950..50a4c955d29 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -796,12 +796,11 @@ impl Dispatch<'_> { } } - static ROOT_KEY: ::ink_primitives::Key = ::ink_primitives::Key::new([0x00; 32]); - fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) { if mutates { - ::ink_storage::traits::push_spread_root::<#storage_ident>( - &contract, &ROOT_KEY + ::ink_storage::traits::push_storage::<#storage_ident>( + &contract, + &<#storage_ident as ::ink_storage::traits::KeyHolder>::KEY, ); } } @@ -813,7 +812,10 @@ impl Dispatch<'_> { ) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> { let mut contract: ::core::mem::ManuallyDrop<#storage_ident> = ::core::mem::ManuallyDrop::new( - ::ink_storage::traits::pull_spread_root::<#storage_ident>(&ROOT_KEY) + ::ink_storage::pull_or_init!( + #storage_ident, + <#storage_ident as ::ink_storage::traits::KeyHolder>::KEY + ) ); match self { diff --git a/crates/lang/codegen/src/generator/metadata.rs b/crates/lang/codegen/src/generator/metadata.rs index 0142643a755..def5eaf5bdc 100644 --- a/crates/lang/codegen/src/generator/metadata.rs +++ b/crates/lang/codegen/src/generator/metadata.rs @@ -46,8 +46,12 @@ impl GenerateCode for Metadata<'_> { const _: () = { #[no_mangle] pub fn __ink_generate_metadata() -> ::ink_metadata::MetadataVersioned { + let layout = #layout; + ::ink_metadata::layout::ValidateLayout::validate(&layout).unwrap_or_else(|error| { + ::core::panic!("metadata ink! generation failed: {}", error) + }); <::ink_metadata::InkProject as ::core::convert::Into<::ink_metadata::MetadataVersioned>>::into( - ::ink_metadata::InkProject::new(#layout, #contract) + ::ink_metadata::InkProject::new(layout, #contract) ) } }; @@ -59,12 +63,21 @@ impl Metadata<'_> { fn generate_layout(&self) -> TokenStream2 { let storage_span = self.contract.module().storage().span(); let storage_ident = self.contract.module().storage().ident(); + let key = quote! { <#storage_ident as ::ink_storage::traits::KeyHolder>::KEY }; + + let layout_key = quote! { + <::ink_metadata::layout::LayoutKey + as ::core::convert::From<::ink_primitives::Key>>::from(#key) + }; quote_spanned!(storage_span=> - <#storage_ident as ::ink_storage::traits::StorageLayout>::layout( - &mut <::ink_primitives::KeyPtr as ::core::convert::From<::ink_primitives::Key>>::from( - <::ink_primitives::Key as ::core::convert::From<[::core::primitive::u8; 32usize]>>::from([0x00_u8; 32usize]) - ) - ) + // Wrap the layout of the contract into the `RootLayout`, because + // contract storage key is reserved for all atomic fields + ::ink_metadata::layout::Layout::Root(::ink_metadata::layout::RootLayout::new( + #layout_key, + <#storage_ident as ::ink_storage::traits::StorageLayout>::layout( + &#key, + ), + )) ) } diff --git a/crates/lang/codegen/src/generator/mod.rs b/crates/lang/codegen/src/generator/mod.rs index a6d68df5e69..960a1b58dd7 100644 --- a/crates/lang/codegen/src/generator/mod.rs +++ b/crates/lang/codegen/src/generator/mod.rs @@ -39,6 +39,7 @@ mod item_impls; mod metadata; mod selector; mod storage; +mod storage_item; mod trait_def; pub use self::{ @@ -66,5 +67,6 @@ pub use self::{ SelectorId, }, storage::Storage, + storage_item::StorageItem, trait_def::TraitDefinition, }; diff --git a/crates/lang/codegen/src/generator/selector.rs b/crates/lang/codegen/src/generator/selector.rs index 1a095646c4b..d21ba632615 100644 --- a/crates/lang/codegen/src/generator/selector.rs +++ b/crates/lang/codegen/src/generator/selector.rs @@ -46,7 +46,7 @@ pub struct SelectorBytes<'a> { } impl GenerateCode for SelectorBytes<'_> { - /// Generates `selector_id!` macro code. + /// Generates `selector_bytes!` macro code. fn generate_code(&self) -> TokenStream2 { let span = self.macro_input.input().span(); let selector_bytes = self.macro_input.selector().hex_lits(); diff --git a/crates/lang/codegen/src/generator/storage.rs b/crates/lang/codegen/src/generator/storage.rs index 973048cbd89..52c9f2d2509 100644 --- a/crates/lang/codegen/src/generator/storage.rs +++ b/crates/lang/codegen/src/generator/storage.rs @@ -93,18 +93,15 @@ impl Storage<'_> { let storage = self.contract.module().storage(); let span = storage.span(); let ident = storage.ident(); + let generics = storage.generics(); let attrs = storage.attrs(); let fields = storage.fields(); quote_spanned!( span => #(#attrs)* - #[cfg_attr( - feature = "std", - derive(::ink_storage::traits::StorageLayout) - )] - #[derive(::ink_storage::traits::SpreadLayout)] + #[::ink_lang::storage_item] #[cfg_attr(test, derive(::core::fmt::Debug))] #[cfg(not(feature = "__ink_dylint_Storage"))] - pub struct #ident { + pub struct #ident #generics { #( #fields ),* } @@ -112,10 +109,6 @@ impl Storage<'_> { impl ::ink_lang::reflect::ContractName for #ident { const NAME: &'static str = ::core::stringify!(#ident); } - - impl ::ink_lang::codegen::ContractRootKey for #ident { - const ROOT_KEY: ::ink_primitives::Key = ::ink_primitives::Key::new([0x00; 32]); - } }; ) } diff --git a/crates/lang/codegen/src/generator/storage_item.rs b/crates/lang/codegen/src/generator/storage_item.rs new file mode 100644 index 00000000000..1d4d8cffc7f --- /dev/null +++ b/crates/lang/codegen/src/generator/storage_item.rs @@ -0,0 +1,248 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::GenerateCode; +use derive_more::From; +use proc_macro2::{ + Ident, + TokenStream as TokenStream2, + TokenStream, +}; +use quote::{ + format_ident, + quote, + quote_spanned, + ToTokens, +}; +use syn::{ + spanned::Spanned, + Data, + DataEnum, + DataStruct, + DataUnion, + Field, + Fields, + Type, +}; + +/// Generates code for the storage item. +#[derive(From, Copy, Clone)] +pub struct StorageItem<'a> { + /// The storage item to generate code for. + item: &'a ir::StorageItem, +} + +impl GenerateCode for StorageItem<'_> { + /// Generates ink! storage item code. + fn generate_code(&self) -> TokenStream2 { + let attrs = self.item.attrs(); + let generated_struct = match self.item.data().clone() { + Data::Struct(struct_item) => self.generate_struct(struct_item), + Data::Enum(enum_item) => self.generate_enum(enum_item), + Data::Union(union_item) => self.generate_union(union_item), + }; + + let mut derive = quote! {}; + if self.item.config().derive() { + derive = quote! { + #[cfg_attr(feature = "std", derive( + ::scale_info::TypeInfo, + ::ink_storage::traits::StorageLayout, + ))] + #[derive( + ::ink_storage::traits::Item, + ::ink_storage::traits::KeyHolder, + ::ink_storage::traits::Storable, + )] + }; + } + + let type_check = self.generate_type_check(); + + quote! { + #type_check + + #(#attrs)* + #derive + #generated_struct + } + } +} + +impl<'a> StorageItem<'a> { + fn generate_struct(&self, struct_item: DataStruct) -> TokenStream2 { + let item = self.item; + let struct_ident = item.ident(); + let vis = item.vis(); + let generics = item.generics(); + let salt = item.salt(); + + let fields = struct_item.fields.iter().enumerate().map(|(i, field)| { + convert_into_storage_field(struct_ident, None, &salt, i, field) + }); + + match struct_item.fields { + Fields::Unnamed(_) => { + quote! { + #vis struct #struct_ident #generics ( + #(#fields),* + ); + } + } + _ => { + quote! { + #vis struct #struct_ident #generics { + #(#fields),* + } + } + } + } + } + + fn generate_enum(&self, enum_item: DataEnum) -> TokenStream2 { + let item = self.item; + let enum_ident = item.ident(); + let vis = item.vis(); + let generics = item.generics(); + let salt = item.salt(); + + let variants = enum_item.variants.into_iter().map(|variant| { + let attrs = variant.attrs; + let variant_ident = &variant.ident; + let discriminant = if let Some((eq, expr)) = variant.discriminant { + quote! { #eq #expr} + } else { + quote! {} + }; + + let fields: Vec<_> = variant + .fields + .iter() + .enumerate() + .map(|(i, field)| { + convert_into_storage_field( + enum_ident, + Some(variant_ident), + &salt, + i, + field, + ) + }) + .collect(); + + let fields = match variant.fields { + Fields::Named(_) => quote! { { #(#fields),* } }, + Fields::Unnamed(_) => quote! { ( #(#fields),* ) }, + Fields::Unit => quote! {}, + }; + + quote! { + #(#attrs)* + #variant_ident #fields #discriminant + } + }); + + quote! { + #vis enum #enum_ident #generics { + #(#variants),* + } + } + } + + fn generate_union(&self, union_item: DataUnion) -> TokenStream2 { + let item = self.item; + let union_ident = item.ident(); + let vis = item.vis(); + let generics = item.generics(); + let salt = item.salt(); + + let fields = union_item + .fields + .named + .iter() + .enumerate() + .map(|(i, field)| { + convert_into_storage_field(union_ident, None, &salt, i, field) + }); + + quote! { + #vis union #union_ident #generics { + #(#fields),* + } + } + } + + fn generate_type_check(&self) -> TokenStream2 { + let fields = self + .item + .all_used_types() + .into_iter() + .enumerate() + .map(|(i, ty)| { + let field_name = format_ident!("field_{}", i); + let span = ty.span(); + quote_spanned!(span => + #field_name: #ty + ) + }); + let generics = self.item.generics(); + let salt = self.item.salt(); + + quote! { + const _: () = { + struct Check #generics { + salt: #salt, + #(#fields),* + } + }; + } + } +} + +fn convert_into_storage_field( + struct_ident: &Ident, + variant_ident: Option<&syn::Ident>, + salt: &TokenStream, + index: usize, + field: &Field, +) -> Field { + let field_name = if let Some(field_ident) = &field.ident { + field_ident.to_string() + } else { + index.to_string() + }; + + let variant_name = if let Some(variant_ident) = variant_ident { + variant_ident.to_string() + } else { + "".to_string() + }; + + let key = ink_primitives::KeyComposer::compute_key( + struct_ident.to_string().as_str(), + variant_name.as_str(), + field_name.as_str(), + ); + + let mut new_field = field.clone(); + let ty = field.ty.clone().to_token_stream(); + let span = field.ty.span(); + let new_ty = Type::Verbatim(quote_spanned!(span => + <#ty as ::ink_storage::traits::AutoItem< + ::ink_storage::traits::ManualKey<#key, #salt>, + >>::Type + )); + new_field.ty = new_ty; + new_field +} diff --git a/crates/lang/codegen/src/generator/trait_def/call_builder.rs b/crates/lang/codegen/src/generator/trait_def/call_builder.rs index 3852a1adc73..2f6fc5e3bb9 100644 --- a/crates/lang/codegen/src/generator/trait_def/call_builder.rs +++ b/crates/lang/codegen/src/generator/trait_def/call_builder.rs @@ -58,16 +58,12 @@ impl GenerateCode for CallBuilder<'_> { fn generate_code(&self) -> TokenStream2 { let struct_definition = self.generate_struct_definition(); let storage_layout_impl = self.generate_storage_layout_impl(); - let spread_layout_impl = self.generate_spread_layout_impl(); - let packed_layout_impl = self.generate_packed_layout_impl(); let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); let to_from_account_id_impls = self.generate_to_from_account_id_impls(); let ink_trait_impl = self.generate_ink_trait_impl(); quote! { #struct_definition #storage_layout_impl - #spread_layout_impl - #packed_layout_impl #auxiliary_trait_impls #to_from_account_id_impls #ink_trait_impl @@ -110,7 +106,10 @@ impl CallBuilder<'_> { /// All calls to types (contracts) implementing the trait will be built by this type. #[doc(hidden)] #[allow(non_camel_case_types)] - #[derive(::scale::Encode, ::scale::Decode)] + #[derive( + ::scale::Encode, + ::scale::Decode, + )] #[repr(transparent)] pub struct #call_builder_ident where @@ -139,92 +138,25 @@ impl CallBuilder<'_> { ::AccountId: ::ink_storage::traits::StorageLayout, { fn layout( - __key_ptr: &mut ::ink_storage::traits::KeyPtr, + __key: &::ink_primitives::Key, ) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new([ - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("account_id"), - <::AccountId - as ::ink_storage::traits::StorageLayout>::layout(__key_ptr) - ) - ]) + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(#call_builder_ident), + [ + ::ink_metadata::layout::FieldLayout::new( + "account_id", + <::AccountId + as ::ink_storage::traits::StorageLayout>::layout(__key) + ) + ] + ) ) } } ) } - /// Generates the `SpreadLayout` trait implementation for the account wrapper. - /// - /// # Note - /// - /// Due to the generic parameter `E` and Rust's default rules for derive generated - /// trait bounds it is not recommended to derive the `SpreadLayout` trait implementation. - fn generate_spread_layout_impl(&self) -> TokenStream2 { - let span = self.span(); - let call_builder_ident = self.ident(); - quote_spanned!(span=> - /// We require this manual implementation since the derive produces incorrect trait bounds. - impl ::ink_storage::traits::SpreadLayout - for #call_builder_ident - where - E: ::ink_env::Environment, - ::AccountId: ::ink_storage::traits::SpreadLayout, - { - const FOOTPRINT: ::core::primitive::u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = false; - - #[inline] - fn pull_spread(ptr: &mut ::ink_primitives::KeyPtr) -> Self { - Self { - account_id: <::AccountId - as ::ink_storage::traits::SpreadLayout>::pull_spread(ptr) - } - } - - #[inline] - fn push_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { - <::AccountId - as ::ink_storage::traits::SpreadLayout>::push_spread(&self.account_id, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { - <::AccountId - as ::ink_storage::traits::SpreadLayout>::clear_spread(&self.account_id, ptr) - } - } - ) - } - - /// Generates the `PackedLayout` trait implementation for the account wrapper. - /// - /// # Note - /// - /// Due to the generic parameter `E` and Rust's default rules for derive generated - /// trait bounds it is not recommended to derive the `PackedLayout` trait implementation. - fn generate_packed_layout_impl(&self) -> TokenStream2 { - let span = self.span(); - let call_builder_ident = self.ident(); - quote_spanned!(span=> - /// We require this manual implementation since the derive produces incorrect trait bounds. - impl ::ink_storage::traits::PackedLayout - for #call_builder_ident - where - E: ::ink_env::Environment, - ::AccountId: ::ink_storage::traits::PackedLayout, - { - #[inline] - fn pull_packed(&mut self, _at: &::ink_primitives::Key) {} - #[inline] - fn push_packed(&self, _at: &::ink_primitives::Key) {} - #[inline] - fn clear_packed(&self, _at: &::ink_primitives::Key) {} - } - ) - } - /// Generates trait implementations for auxiliary traits for the account wrapper. /// /// # Note diff --git a/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs index 1d8b92a774d..70bdb9a3a4d 100644 --- a/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs +++ b/crates/lang/codegen/src/generator/trait_def/call_forwarder.rs @@ -59,8 +59,6 @@ impl GenerateCode for CallForwarder<'_> { fn generate_code(&self) -> TokenStream2 { let struct_definition = self.generate_struct_definition(); let storage_layout_impl = self.generate_storage_layout_impl(); - let spread_layout_impl = self.generate_spread_layout_impl(); - let packed_layout_impl = self.generate_packed_layout_impl(); let auxiliary_trait_impls = self.generate_auxiliary_trait_impls(); let to_from_account_id_impls = self.generate_to_from_account_id_impls(); let call_builder_impl = self.generate_call_builder_trait_impl(); @@ -68,8 +66,6 @@ impl GenerateCode for CallForwarder<'_> { quote! { #struct_definition #storage_layout_impl - #spread_layout_impl - #packed_layout_impl #auxiliary_trait_impls #to_from_account_id_impls #call_builder_impl @@ -114,7 +110,10 @@ impl CallForwarder<'_> { /// will be handled by this type. #[doc(hidden)] #[allow(non_camel_case_types)] - #[derive(::scale::Encode, ::scale::Decode)] + #[derive( + ::scale::Encode, + ::scale::Decode, + )] #[repr(transparent)] pub struct #call_forwarder_ident where @@ -143,83 +142,15 @@ impl CallForwarder<'_> { ::AccountId: ::ink_storage::traits::StorageLayout, { fn layout( - __key_ptr: &mut ::ink_storage::traits::KeyPtr, + __key: &::ink_primitives::Key, ) -> ::ink_metadata::layout::Layout { <::Builder - as ::ink_storage::traits::StorageLayout>::layout(__key_ptr) + as ::ink_storage::traits::StorageLayout>::layout(__key) } } ) } - /// Generates the `SpreadLayout` trait implementation for the account wrapper. - /// - /// # Note - /// - /// Due to the generic parameter `E` and Rust's default rules for derive generated - /// trait bounds it is not recommended to derive the `SpreadLayout` trait implementation. - fn generate_spread_layout_impl(&self) -> TokenStream2 { - let span = self.span(); - let call_forwarder_ident = self.ident(); - quote_spanned!(span=> - impl ::ink_storage::traits::SpreadLayout - for #call_forwarder_ident - where - E: ::ink_env::Environment, - ::AccountId: ::ink_storage::traits::SpreadLayout, - { - const FOOTPRINT: ::core::primitive::u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = false; - - #[inline] - fn pull_spread(ptr: &mut ::ink_primitives::KeyPtr) -> Self { - Self { - builder: <::Builder - as ::ink_storage::traits::SpreadLayout>::pull_spread(ptr) - } - } - - #[inline] - fn push_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { - <::Builder - as ::ink_storage::traits::SpreadLayout>::push_spread(&self.builder, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut ::ink_primitives::KeyPtr) { - <::Builder - as ::ink_storage::traits::SpreadLayout>::clear_spread(&self.builder, ptr) - } - } - ) - } - - /// Generates the `PackedLayout` trait implementation for the account wrapper. - /// - /// # Note - /// - /// Due to the generic parameter `E` and Rust's default rules for derive generated - /// trait bounds it is not recommended to derive the `PackedLayout` trait implementation. - fn generate_packed_layout_impl(&self) -> TokenStream2 { - let span = self.span(); - let call_forwarder_ident = self.ident(); - quote_spanned!(span=> - impl ::ink_storage::traits::PackedLayout - for #call_forwarder_ident - where - E: ::ink_env::Environment, - ::AccountId: ::ink_storage::traits::PackedLayout, - { - #[inline] - fn pull_packed(&mut self, _at: &::ink_primitives::Key) {} - #[inline] - fn push_packed(&self, _at: &::ink_primitives::Key) {} - #[inline] - fn clear_packed(&self, _at: &::ink_primitives::Key) {} - } - ) - } - /// Generates trait implementations for auxiliary traits for the account wrapper. /// /// # Note diff --git a/crates/lang/codegen/src/lib.rs b/crates/lang/codegen/src/lib.rs index 27b82bec914..b996b1a4f95 100644 --- a/crates/lang/codegen/src/lib.rs +++ b/crates/lang/codegen/src/lib.rs @@ -35,6 +35,10 @@ impl<'a> CodeGenerator for &'a ir::Contract { type Generator = generator::Contract<'a>; } +impl<'a> CodeGenerator for &'a ir::StorageItem { + type Generator = generator::StorageItem<'a>; +} + impl<'a> CodeGenerator for &'a ir::InkTraitDefinition { type Generator = generator::TraitDefinition<'a>; } diff --git a/crates/lang/ir/Cargo.toml b/crates/lang/ir/Cargo.toml index aaf91ca49e1..c8dbb87b6f6 100644 --- a/crates/lang/ir/Cargo.toml +++ b/crates/lang/ir/Cargo.toml @@ -18,6 +18,7 @@ include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] name = "ink_lang_ir" [dependencies] +ink_storage_codegen = { version = "4.0.0", path = "../../storage/codegen" } quote = "1" syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"] } proc-macro2 = "1.0" diff --git a/crates/lang/ir/src/ir/item/storage.rs b/crates/lang/ir/src/ir/item/storage.rs index 48dfa7d82dc..52a16f35bd1 100644 --- a/crates/lang/ir/src/ir/item/storage.rs +++ b/crates/lang/ir/src/ir/item/storage.rs @@ -91,12 +91,6 @@ impl TryFrom for Storage { } }, )?; - if !item_struct.generics.params.is_empty() { - return Err(format_err_spanned!( - item_struct.generics.params, - "generic ink! storage structs are not supported", - )) - } utils::ensure_pub_visibility("storage structs", struct_span, &item_struct.vis)?; Ok(Self { ast: syn::ItemStruct { @@ -118,6 +112,11 @@ impl Storage { &self.ast.ident } + /// Returns the generics of the storage struct. + pub fn generics(&self) -> &syn::Generics { + &self.ast.generics + } + /// Returns an iterator yielding all fields of the storage struct. pub fn fields(&self) -> syn::punctuated::Iter { self.ast.fields.iter() @@ -205,19 +204,6 @@ mod tests { ) } - #[test] - fn generic_storage_fails() { - assert_try_from_fails( - syn::parse_quote! { - #[ink(storage)] - pub struct GenericStorage { - field_1: T, - } - }, - "generic ink! storage structs are not supported", - ) - } - #[test] fn non_pub_storage_struct() { assert_try_from_fails( diff --git a/crates/lang/ir/src/ir/mod.rs b/crates/lang/ir/src/ir/mod.rs index 18c625241e0..1f09ca5f7af 100644 --- a/crates/lang/ir/src/ir/mod.rs +++ b/crates/lang/ir/src/ir/mod.rs @@ -25,6 +25,7 @@ mod item; mod item_impl; mod item_mod; mod selector; +mod storage_item; mod trait_def; pub mod utils; @@ -97,6 +98,7 @@ pub use self::{ SelectorMacro, TraitPrefix, }, + storage_item::StorageItem, trait_def::{ InkItemTrait, InkTraitDefinition, diff --git a/crates/lang/ir/src/ir/storage_item/config.rs b/crates/lang/ir/src/ir/storage_item/config.rs new file mode 100644 index 00000000000..3a58b85aff7 --- /dev/null +++ b/crates/lang/ir/src/ir/storage_item/config.rs @@ -0,0 +1,85 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{ + ast, + error::ExtError as _, +}; +use syn::spanned::Spanned; + +/// The ink! configuration. +#[derive(Debug, Default, PartialEq, Eq)] +pub struct StorageItemConfig { + /// If set to `true`, all storage related traits are implemented automatically, + /// this is the default value. + /// If set to `false`, implementing all storage traits is disabled. In some cases + /// this can be helpful to override the default implementation of the trait. + derive: bool, +} + +/// Return an error to notify about duplicate ink! ink storage configuration arguments. +fn duplicate_config_err(fst: F, snd: S, name: &str) -> syn::Error +where + F: Spanned, + S: Spanned, +{ + format_err!( + snd.span(), + "encountered duplicate ink! storage item `{}` configuration argument", + name, + ) + .into_combine(format_err!( + fst.span(), + "first `{}` configuration argument here", + name + )) +} + +impl TryFrom for StorageItemConfig { + type Error = syn::Error; + + fn try_from(args: ast::AttributeArgs) -> Result { + let mut derive: Option = None; + for arg in args.into_iter() { + if arg.name.is_ident("derive") { + if let Some(lit_bool) = derive { + return Err(duplicate_config_err(lit_bool, arg, "derive")) + } + if let ast::PathOrLit::Lit(syn::Lit::Bool(lit_bool)) = &arg.value { + derive = Some(lit_bool.clone()) + } else { + return Err(format_err_spanned!( + arg, + "expected a bool literal for `derive` ink! storage item configuration argument", + )) + } + } else { + return Err(format_err_spanned!( + arg, + "encountered unknown or unsupported ink! storage item configuration argument", + )) + } + } + Ok(StorageItemConfig { + derive: derive.map(|lit_bool| lit_bool.value).unwrap_or(true), + }) + } +} + +impl StorageItemConfig { + /// Returns the derive configuration argument. + pub fn derive(&self) -> bool { + self.derive + } +} diff --git a/crates/lang/ir/src/ir/storage_item/mod.rs b/crates/lang/ir/src/ir/storage_item/mod.rs new file mode 100644 index 00000000000..3d3304941dd --- /dev/null +++ b/crates/lang/ir/src/ir/storage_item/mod.rs @@ -0,0 +1,89 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod config; + +use config::StorageItemConfig; +use ink_storage_codegen::DeriveUtils; +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + ToTokens, +}; + +/// A checked ink! storage item with its configuration. +pub struct StorageItem { + ast: syn::DeriveInput, + config: StorageItemConfig, +} + +impl StorageItem { + /// Returns `Ok` if the input matches all requirements for an ink! storage item. + pub fn new(config: TokenStream2, item: TokenStream2) -> Result { + let ast = syn::parse2::(item)?; + let parsed_config = syn::parse2::(config)?; + let config = StorageItemConfig::try_from(parsed_config)?; + + Ok(Self { ast, config }) + } + + /// Returns AST. + pub fn ast(&self) -> &syn::DeriveInput { + &self.ast + } + + /// Returns all types that were used in the storage declaration. + pub fn all_used_types(&self) -> Vec { + self.ast.all_types() + } + + /// Returns the config of the storage. + pub fn config(&self) -> &StorageItemConfig { + &self.config + } + + /// Returns the visibility of the storage. + pub fn vis(&self) -> &syn::Visibility { + &self.ast.vis + } + + /// Returns the attributes of the storage. + pub fn attrs(&self) -> &[syn::Attribute] { + &self.ast.attrs + } + + /// Returns the identifier of the storage. + pub fn ident(&self) -> &syn::Ident { + &self.ast.ident + } + + /// Returns the generics of the storage. + pub fn generics(&self) -> &syn::Generics { + &self.ast.generics + } + + /// Returns data of the storage. + pub fn data(&self) -> &syn::Data { + &self.ast.data + } + + /// Returns salt for storage key + pub fn salt(&self) -> TokenStream2 { + if let Some(param) = self.ast.find_salt() { + param.ident.to_token_stream() + } else { + quote! { () } + } + } +} diff --git a/crates/lang/ir/src/lib.rs b/crates/lang/ir/src/lib.rs index 8dfbc1c0ed6..229f7c0b39c 100644 --- a/crates/lang/ir/src/lib.rs +++ b/crates/lang/ir/src/lib.rs @@ -73,6 +73,7 @@ pub use self::{ Selector, SelectorMacro, Storage, + StorageItem, Visibility, }, literal::HexLiteral, diff --git a/crates/lang/macro/Cargo.toml b/crates/lang/macro/Cargo.toml index 9cf13db2980..de53351cd45 100644 --- a/crates/lang/macro/Cargo.toml +++ b/crates/lang/macro/Cargo.toml @@ -24,6 +24,7 @@ syn = "1" proc-macro2 = "1" [dev-dependencies] +ink_prelude = { version = "4.0.0", path = "../../prelude/" } ink_metadata = { version = "4.0.0", path = "../../metadata/" } ink_env = { version = "4.0.0", path = "../../env/" } ink_storage = { version = "4.0.0", path = "../../storage/" } diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index fa7cf4f7749..c004c05ee1e 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -19,6 +19,7 @@ mod chain_extension; mod contract; mod ink_test; mod selector; +mod storage_item; mod trait_def; use proc_macro::TokenStream; @@ -665,6 +666,148 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { trait_def::analyze(attr.into(), item.into()).into() } +/// Prepares the type to be fully compatible and usable with the storage. +/// It implements all necessary traits and calculates the storage key for types. +/// Packed types don't have a storage key, but non-packed types +/// (like `Mapping`, `Lazy` etc.) require calculating the storage key during compilation. +/// +/// Consider annotating structs and enums that are intented to be a part of +/// the storage with this macro. If the type is packed then the usage of the +/// macro is optional. +/// +/// If the type is non-packed it is best to rely on automatic storage key +/// calculation. The storage key can also be specified manually with the +/// generic `KEY` parameter though. +/// +/// The macro should be called before `derive` macros because it can change the type. +/// +/// All required traits can be: +/// - Derived manually via `#[derive(...)]`. +/// - Derived automatically via deriving of `scale::Decode` and `scale::Encode`. +/// - Derived via this macro. +/// +/// # Example +/// +/// ## Trait implementation +/// +/// ``` +/// use ink_prelude::vec::Vec; +/// use ink_storage::{ +/// Lazy, +/// Mapping, +/// }; +/// use ink_storage::traits::{ +/// KeyHolder, +/// Item, +/// Storable, +/// }; +/// +/// #[derive(scale::Decode, scale::Encode)] +/// struct Packed { +/// s1: u128, +/// s2: Vec, +/// // Fails because `Item` is only implemented for `Vec` where `T: Packed`. +/// // s3: Vec, +/// } +/// +/// #[derive(scale::Decode, scale::Encode)] +/// #[cfg_attr( +/// feature = "std", +/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +/// )] +/// struct PackedManual { +/// s1: u32, +/// s2: Vec<(u128, String)>, +/// // Fails because `Item` is only implemented for `Vec` where `T: Packed`. +/// // s3: Vec, +/// } +/// +/// #[derive(scale::Decode, scale::Encode)] +/// #[cfg_attr( +/// feature = "std", +/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +/// )] +/// struct PackedGeneric { +/// s1: (u128, bool), +/// s2: Vec, +/// s3: String, +/// } +/// +/// #[ink_lang::storage_item(derive = false)] +/// #[derive(Storable, Item, KeyHolder)] +/// #[cfg_attr( +/// feature = "std", +/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +/// )] +/// struct NonPackedGeneric { +/// s1: u32, +/// s2: T, +/// s3: Mapping, +/// } +/// +/// #[derive(scale::Decode, scale::Encode)] +/// #[cfg_attr( +/// feature = "std", +/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +/// )] +/// struct PackedComplex { +/// s1: u128, +/// s2: Vec, +/// s3: Vec, +/// } +/// +/// #[ink_lang::storage_item] +/// struct NonPacked { +/// s1: Mapping, +/// s2: Lazy, +/// } +/// +/// #[ink_lang::storage_item] +/// struct NonPackedComplex { +/// s1: (String, u128, Packed), +/// s2: Mapping, +/// s3: Lazy, +/// s4: Mapping, +/// s5: Lazy, +/// s6: PackedGeneric, +/// s7: NonPackedGeneric, +/// // Fails because: the trait `ink_storage::traits::Packed` is not implemented for `NonPacked` +/// // s8: Mapping, +/// } +/// ``` +/// +/// ## Header Arguments +/// +/// The `#[ink::storage_item]` macro can be provided with some additional comma-separated +/// header arguments: +/// +/// - `derive: bool` +/// +/// The `derive` configuration parameter is used to enable/disable auto deriving of +/// all required storage traits. +/// +/// **Usage Example:** +/// ``` +/// use ink_storage::Mapping; +/// use ink_storage::traits::{ +/// Item, +/// KeyHolder, +/// Storable, +/// }; +/// #[ink_lang::storage_item(derive = false)] +/// #[derive(Item, Storable, KeyHolder)] +/// struct NonPackedGeneric { +/// s1: u32, +/// s2: Mapping, +/// } +/// ``` +/// +/// **Default value:** true. +#[proc_macro_attribute] +pub fn storage_item(attr: TokenStream, item: TokenStream) -> TokenStream { + storage_item::generate(attr.into(), item.into()).into() +} + /// Defines a unit test that makes use of ink!'s off-chain testing capabilities. /// /// If your unit test does not require the existence of an off-chain environment @@ -786,7 +929,7 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// A chain extension method that is flagged with `handle_status = false` assumes that the returned error code /// will always indicate success. Therefore it will always load and decode the output buffer and loses -/// the `E: From` constraint for the call. /// /// ## Details: `returns_result` /// diff --git a/crates/storage/src/traits/layout/tests.rs b/crates/lang/macro/src/storage_item.rs similarity index 54% rename from crates/storage/src/traits/layout/tests.rs rename to crates/lang/macro/src/storage_item.rs index 6cac182901d..ed7861ea8f8 100644 --- a/crates/storage/src/traits/layout/tests.rs +++ b/crates/lang/macro/src/storage_item.rs @@ -11,3 +11,22 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +use ink_lang_codegen::generate_code; +use proc_macro2::TokenStream as TokenStream2; +use syn::Result; + +pub fn generate(config: TokenStream2, input: TokenStream2) -> TokenStream2 { + match generate_or_err(config, input) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error(), + } +} + +pub fn generate_or_err( + config: TokenStream2, + input: TokenStream2, +) -> Result { + let storage_item = ink_lang_ir::StorageItem::new(config, input)?; + Ok(generate_code(&storage_item)) +} diff --git a/crates/lang/src/codegen/dispatch/execution.rs b/crates/lang/src/codegen/dispatch/execution.rs index 749b1d841dc..66963fc46f7 100644 --- a/crates/lang/src/codegen/dispatch/execution.rs +++ b/crates/lang/src/codegen/dispatch/execution.rs @@ -24,28 +24,12 @@ use ink_env::{ Environment, ReturnFlags, }; -use ink_primitives::{ - Key, - KeyPtr, -}; use ink_storage::traits::{ - push_spread_root, - SpreadAllocate, - SpreadLayout, + push_storage, + KeyHolder, + Storable, }; - -/// The root key of the ink! smart contract. -/// -/// # Note -/// -/// - This is the key where storage allocation, pushing and pulling is rooted -/// using the `SpreadLayout` and `SpreadAllocate` traits primarily. -/// - This trait is automatically implemented by the ink! codegen. -/// - The existence of this trait allows to customize the root key in future -/// versions of ink! if needed. -pub trait ContractRootKey { - const ROOT_KEY: Key; -} +use scale::Encode; /// Returns `Ok` if the caller did not transfer additional value to the callee. /// @@ -73,9 +57,9 @@ where #[inline] pub fn execute_constructor(f: F) -> Result<(), DispatchError> where - Contract: SpreadLayout + ContractRootKey + ContractEnv, + Contract: Storable + KeyHolder + ContractEnv, F: FnOnce() -> R, - as ConstructorReturnType>::ReturnValue: scale::Encode, + as ConstructorReturnType>::ReturnValue: Encode, private::Seal: ConstructorReturnType, { let result = ManuallyDrop::new(private::Seal(f())); @@ -84,8 +68,7 @@ where // Constructor is infallible or is fallible but succeeded. // // This requires us to sync back the changes of the contract storage. - let root_key = ::ROOT_KEY; - push_spread_root::(contract, &root_key); + push_storage::(contract, &Contract::KEY); Ok(()) } Err(_) => { @@ -102,32 +85,6 @@ where } } -/// Initializes the ink! contract using the given initialization routine. -/// -/// # Note -/// -/// - This uses `SpreadAllocate` trait in order to default initialize the -/// ink! smart contract before calling the initialization routine. -/// - This either returns `Contract` or `Result` depending -/// on the return type `R` of the initializer closure `F`. -/// If `R` is `()` then `Contract` is returned and if `R` is any type of -/// `Result<(), E>` then `Result` is returned. -/// Other return types for `F` than the ones listed above are not allowed. -#[inline] -pub fn initialize_contract( - initializer: F, -) -> >::Wrapped -where - Contract: ContractRootKey + SpreadAllocate, - F: FnOnce(&mut Contract) -> R, - R: InitializerReturnType, -{ - let mut key_ptr = KeyPtr::from(::ROOT_KEY); - let mut instance = ::allocate_spread(&mut key_ptr); - let result = initializer(&mut instance); - result.into_wrapped(instance) -} - mod private { /// Seals the implementation of `ContractInitializerReturnType`. pub trait Sealed {} diff --git a/crates/lang/src/codegen/dispatch/mod.rs b/crates/lang/src/codegen/dispatch/mod.rs index 3908e094fba..78e0499542d 100644 --- a/crates/lang/src/codegen/dispatch/mod.rs +++ b/crates/lang/src/codegen/dispatch/mod.rs @@ -20,8 +20,6 @@ pub use self::{ execution::{ deny_payment, execute_constructor, - initialize_contract, - ContractRootKey, }, info::ContractCallBuilder, type_check::{ diff --git a/crates/lang/src/codegen/mod.rs b/crates/lang/src/codegen/mod.rs index e22b0079021..aec55fecee7 100644 --- a/crates/lang/src/codegen/mod.rs +++ b/crates/lang/src/codegen/mod.rs @@ -25,9 +25,7 @@ pub use self::{ dispatch::{ deny_payment, execute_constructor, - initialize_contract, ContractCallBuilder, - ContractRootKey, DispatchInput, DispatchOutput, }, diff --git a/crates/lang/src/lib.rs b/crates/lang/src/lib.rs index b65981a7189..e0e3cd85c74 100644 --- a/crates/lang/src/lib.rs +++ b/crates/lang/src/lib.rs @@ -21,13 +21,6 @@ pub mod result_info; #[cfg_attr(not(feature = "show-codegen-docs"), doc(hidden))] pub mod codegen; -/// Utility functions for contract development. -pub mod utils { - // We want to expose this function without making users go through - // the `codgen` module - pub use super::codegen::initialize_contract; -} - pub mod reflect; mod chain_extension; @@ -48,6 +41,7 @@ pub use ink_lang_macro::{ contract, selector_bytes, selector_id, + storage_item, test, trait_definition, }; diff --git a/crates/lang/tests/compile_tests.rs b/crates/lang/tests/compile_tests.rs index b24bb0409eb..5a18e4c63f0 100644 --- a/crates/lang/tests/compile_tests.rs +++ b/crates/lang/tests/compile_tests.rs @@ -28,6 +28,9 @@ fn ui_tests() { t.pass("tests/ui/contract/pass/*.rs"); t.compile_fail("tests/ui/contract/fail/*.rs"); + t.pass("tests/ui/storage_item/pass/*.rs"); + t.compile_fail("tests/ui/storage_item/fail/*.rs"); + t.pass("tests/ui/trait_def/pass/*.rs"); t.compile_fail("tests/ui/trait_def/fail/*.rs"); diff --git a/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr index 31684c857ca..0a644b1c6f9 100644 --- a/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr +++ b/crates/lang/tests/ui/contract/fail/message-returns-non-codec.stderr @@ -60,7 +60,7 @@ error[E0599]: the method `fire` exists for struct `ink_env::call::CallBuilder $CARGO/parity-scale-codec-3.1.5/src/codec.rs + --> $CARGO/parity-scale-codec-3.1.2/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs index f1d50541ae6..54d25588462 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -2,14 +2,11 @@ use ink_lang as ink; #[ink::contract] mod erc20 { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; /// A simple ERC-20 contract. #[ink(storage)] - #[derive(SpreadAllocate)] + #[derive(Default)] pub struct Erc20 { /// Total token supply. total_supply: Balance, @@ -58,9 +55,9 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(initial_supply: Balance) -> Self { - ink_lang::utils::initialize_contract(|contract| { - Self::new_init(contract, initial_supply) - }) + let mut instance = Self::default(); + instance.new_init(initial_supply); + instance } /// Default initializes the ERC-20 contract with the specified initial supply. diff --git a/crates/lang/tests/ui/contract/pass/example-erc721-works.rs b/crates/lang/tests/ui/contract/pass/example-erc721-works.rs index 3e4c4fc3a7f..00942ac5a1a 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc721-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc721-works.rs @@ -2,10 +2,7 @@ use ink_lang as ink; #[ink::contract] mod erc721 { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; use scale::{ Decode, @@ -16,7 +13,7 @@ mod erc721 { pub type TokenId = u32; #[ink(storage)] - #[derive(Default, SpreadAllocate)] + #[derive(Default)] pub struct Erc721 { /// Mapping from token to owner. token_owner: Mapping, @@ -77,9 +74,7 @@ mod erc721 { /// Creates a new ERC-721 token contract. #[ink(constructor)] pub fn new() -> Self { - // This call is required in order to correctly initialize the - // `Mapping`s of our contract. - ink_lang::utils::initialize_contract(|_| {}) + Self::default() } /// Returns the balance of the owner. diff --git a/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs b/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs index dc86aab8a7a..c9b1ce530b3 100644 --- a/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs +++ b/crates/lang/tests/ui/contract/pass/storage-packed-fields.rs @@ -2,26 +2,15 @@ use ink_lang as ink; #[ink::contract] mod contract { - use ink_storage::traits::{ - PackedLayout, - SpreadLayout, - StorageLayout, - }; + use ink_storage::traits::StorageLayout; #[ink(storage)] pub struct Contract { packed: PackedFields, } - #[derive( - Debug, - Default, - SpreadLayout, - PackedLayout, - StorageLayout, - scale::Encode, - scale::Decode, - )] + #[derive(Debug, Default, scale::Decode, scale::Encode)] + #[cfg_attr(feature = "std", derive(scale_info::TypeInfo, StorageLayout))] pub struct PackedFields { field_1: i8, field_2: i16, diff --git a/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.rs b/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.rs new file mode 100644 index 00000000000..81de75a1d0e --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.rs @@ -0,0 +1,9 @@ +#[ink_lang::storage_item(derive = "false")] +#[derive(Default)] +struct Contract> { + a: u16, + b: u64, + c: u128, +} + +fn main() {} diff --git a/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.stderr b/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.stderr new file mode 100644 index 00000000000..71047ce2be5 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/argument_derive_invalid_type.stderr @@ -0,0 +1,5 @@ +error: expected a bool literal for `derive` ink! storage item configuration argument + --> tests/ui/storage_item/fail/argument_derive_invalid_type.rs:1:26 + | +1 | #[ink_lang::storage_item(derive = "false")] + | ^^^^^^^^^^^^^^^^ diff --git a/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.rs b/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.rs new file mode 100644 index 00000000000..5322851d07b --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.rs @@ -0,0 +1,9 @@ +#[ink_lang::storage_item(derive)] +#[derive(Default)] +struct Contract> { + a: u16, + b: u64, + c: u128, +} + +fn main() {} diff --git a/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.stderr b/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.stderr new file mode 100644 index 00000000000..3b0ab7c559e --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/argument_derive_missing_arg.stderr @@ -0,0 +1,5 @@ +error: ink! config options require an argument separated by '=' + --> tests/ui/storage_item/fail/argument_derive_missing_arg.rs:1:26 + | +1 | #[ink_lang::storage_item(derive)] + | ^^^^^^ diff --git a/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.rs b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.rs new file mode 100644 index 00000000000..cd20346e6a5 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.rs @@ -0,0 +1,14 @@ +use ink_prelude::vec::Vec; +use ink_storage::Lazy; + +#[ink_lang::storage_item] +struct NonPacked { + a: Lazy, +} + +#[ink_lang::storage_item] +struct Contract { + a: Vec, +} + +fn main() {} diff --git a/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.stderr b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.stderr new file mode 100644 index 00000000000..cdef4fce001 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_1.stderr @@ -0,0 +1,118 @@ +error[E0277]: the trait bound `Vec: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:11:8 + | +11 | a: Vec, + | ^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `Vec` + | + = help: the trait `Decode` is implemented for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` + +error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:11:8 + | +11 | a: Vec, + | ^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `[NonPacked]` + | + = help: the following other types implement trait `Encode`: + [T; N] + [T] + = note: required because of the requirements on the impl of `Encode` for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` + +error[E0277]: the trait bound `Vec: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `Vec` + | + = help: the trait `Decode` is implemented for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Storable` + --> $WORKSPACE/crates/storage/src/traits/storage.rs + | + | pub trait Storable: Sized { + | ^^^^^ required by this bound in `Storable` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `[NonPacked]` + | + = help: the following other types implement trait `Encode`: + [T; N] + [T] + = note: required because of the requirements on the impl of `Encode` for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Storable` + --> $WORKSPACE/crates/storage/src/traits/storage.rs + | + | pub trait Storable: Sized { + | ^^^^^ required by this bound in `Storable` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `Vec: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `Vec` + | + = help: the trait `Decode` is implemented for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Result` + --> $RUST/core/src/result.rs + | + | pub enum Result { + | ^ required by this bound in `Result` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `[NonPacked]` + | + = help: the following other types implement trait `Encode`: + [T; N] + [T] + = note: required because of the requirements on the impl of `Encode` for `Vec` + = note: required because of the requirements on the impl of `Packed` for `Vec` + = note: required because of the requirements on the impl of `Item<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoItem>` for `Vec` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Result` + --> $RUST/core/src/result.rs + | + | pub enum Result { + | ^ required by this bound in `Result` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.rs b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.rs new file mode 100644 index 00000000000..876dfff68ac --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.rs @@ -0,0 +1,14 @@ +use ink_prelude::collections::BTreeMap; +use ink_storage::Lazy; + +#[ink_lang::storage_item] +struct NonPacked { + a: Lazy, +} + +#[ink_lang::storage_item] +struct Contract { + a: BTreeMap, +} + +fn main() {} diff --git a/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.stderr b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.stderr new file mode 100644 index 00000000000..ce6507c03ff --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.stderr @@ -0,0 +1,109 @@ +error[E0277]: the trait bound `BTreeMap: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:11:8 + | +11 | a: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `BTreeMap` + | + = help: the trait `Decode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` + +error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:11:8 + | +11 | a: BTreeMap, + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `BTreeMap` + | + = help: the trait `Encode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` + +error[E0277]: the trait bound `BTreeMap: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `BTreeMap` + | + = help: the trait `Decode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Storable` + --> $WORKSPACE/crates/storage/src/traits/storage.rs + | + | pub trait Storable: Sized { + | ^^^^^ required by this bound in `Storable` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `BTreeMap` + | + = help: the trait `Encode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Storable` + --> $WORKSPACE/crates/storage/src/traits/storage.rs + | + | pub trait Storable: Sized { + | ^^^^^ required by this bound in `Storable` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `BTreeMap: Decode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Decode` is not implemented for `BTreeMap` + | + = help: the trait `Decode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Result` + --> $RUST/core/src/result.rs + | + | pub enum Result { + | ^ required by this bound in `Result` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:9:1 + | +9 | #[ink_lang::storage_item] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Encode` is not implemented for `BTreeMap` + | + = help: the trait `Encode` is implemented for `BTreeMap` + = note: required because of the requirements on the impl of `Packed` for `BTreeMap` + = note: required because of the requirements on the impl of `Item<()>` for `BTreeMap` + = note: required because of the requirements on the impl of `AutoItem>` for `BTreeMap` +note: required because it appears within the type `Contract` + --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 + | +10 | struct Contract { + | ^^^^^^^^ +note: required by a bound in `Result` + --> $RUST/core/src/result.rs + | + | pub enum Result { + | ^ required by this bound in `Result` + = note: this error originates in the derive macro `::ink_storage::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs b/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs new file mode 100644 index 00000000000..da1cf6eef91 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs @@ -0,0 +1,16 @@ +use ink_storage::{ + traits::Packed, + Lazy, +}; + +#[ink_lang::storage_item] +#[derive(Default)] +struct NonPacked { + a: Lazy, +} + +fn consume_packed() {} + +fn main() { + let _ = consume_packed::(); +} diff --git a/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.stderr b/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.stderr new file mode 100644 index 00000000000..e91f54e5cef --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/packed_is_not_derived_automatically.stderr @@ -0,0 +1,41 @@ +error[E0277]: the trait bound `NonPacked: WrapperTypeDecode` is not satisfied + --> tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs:15:30 + | +15 | let _ = consume_packed::(); + | ^^^^^^^^^ the trait `WrapperTypeDecode` is not implemented for `NonPacked` + | + = help: the following other types implement trait `WrapperTypeDecode`: + Arc + Box + Rc + = note: required because of the requirements on the impl of `Decode` for `NonPacked` + = note: required because of the requirements on the impl of `Packed` for `NonPacked` +note: required by a bound in `consume_packed` + --> tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs:12:22 + | +12 | fn consume_packed() {} + | ^^^^^^ required by this bound in `consume_packed` + +error[E0277]: the trait bound `NonPacked: WrapperTypeEncode` is not satisfied + --> tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs:15:30 + | +15 | let _ = consume_packed::(); + | ^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `NonPacked` + | + = help: the following other types implement trait `WrapperTypeEncode`: + &T + &mut T + Arc + Box + Cow<'a, T> + Rc + String + Vec + parity_scale_codec::Ref<'a, T, U> + = note: required because of the requirements on the impl of `Encode` for `NonPacked` + = note: required because of the requirements on the impl of `Packed` for `NonPacked` +note: required by a bound in `consume_packed` + --> tests/ui/storage_item/fail/packed_is_not_derived_automatically.rs:12:22 + | +12 | fn consume_packed() {} + | ^^^^^^ required by this bound in `consume_packed` diff --git a/crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs b/crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs new file mode 100644 index 00000000000..10622990993 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs @@ -0,0 +1,30 @@ +use ink_storage::traits::{ + KeyHolder, + ManualKey, + Storable, +}; + +#[ink_lang::storage_item(derive = false)] +#[derive(Default)] +struct Contract> { + a: u16, + b: u64, + c: u128, +} + +// Disabling of deriving allow to implement the trait manually +impl Storable for Contract { + fn encode(&self, _dest: &mut T) {} + + fn decode(_input: &mut I) -> Result { + Ok(Self { + a: Default::default(), + b: Default::default(), + c: Default::default(), + }) + } +} + +fn main() { + let _: Result>, _> = Storable::decode(&mut &[][..]); +} diff --git a/crates/lang/tests/ui/storage_item/pass/complex_non_packed_enum.rs b/crates/lang/tests/ui/storage_item/pass/complex_non_packed_enum.rs new file mode 100644 index 00000000000..1c6ea5b81b2 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/complex_non_packed_enum.rs @@ -0,0 +1,154 @@ +use ink_prelude::vec::Vec; +use ink_primitives::KeyComposer; +use ink_storage::{ + traits::{ + AutoKey, + KeyHolder, + }, + Lazy, + Mapping, +}; + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +enum Packed { + #[default] + None, + A(u8), + B(u16), + C(u32), + D(u64), + E(u128), + F(String), + G { + a: u8, + b: String, + }, + H((u16, u32)), +} + +#[ink_lang::storage_item] +#[derive(Default)] +enum NonPacked { + #[default] + None, + A(Mapping), + B(Lazy), + C(Lazy), + D(Lazy>), + E(Mapping), + F { + a: Mapping, + }, +} + +#[ink_lang::storage_item] +#[derive(Default)] +struct Contract { + a: Lazy, +} + +fn main() { + ink_env::test::run_test::(|_| { + let mut contract = Contract::default(); + assert_eq!(contract.key(), 0); + + // contract.a + assert_eq!(contract.a.key(), KeyComposer::from_str("Contract::a")); + assert_eq!( + contract.a.get_or_default().key(), + KeyComposer::from_str("Contract::a"), + ); + + contract.a.set(&NonPacked::<_>::A(Default::default())); + let variant = if let NonPacked::<_>::A(variant) = contract.a.get() { + variant + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::A::0"), + KeyComposer::from_str("Contract::a") + ), + ); + + contract.a.set(&NonPacked::<_>::B(Default::default())); + let variant = if let NonPacked::<_>::B(variant) = contract.a.get() { + variant + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::B::0"), + KeyComposer::from_str("Contract::a") + ), + ); + + contract.a.set(&NonPacked::<_>::C(Default::default())); + let variant = if let NonPacked::<_>::C(variant) = contract.a.get() { + variant + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::C::0"), + KeyComposer::from_str("Contract::a") + ), + ); + + contract.a.set(&NonPacked::<_>::D(Default::default())); + let variant = if let NonPacked::<_>::D(variant) = contract.a.get() { + variant + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::D::0"), + KeyComposer::from_str("Contract::a") + ), + ); + + contract.a.set(&NonPacked::<_>::E(Default::default())); + let variant = if let NonPacked::<_>::E(variant) = contract.a.get() { + variant + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::E::0"), + KeyComposer::from_str("Contract::a") + ) + ); + + contract.a.set(&NonPacked::<_>::F { + a: Default::default(), + }); + let variant = if let NonPacked::<_>::F { a } = contract.a.get() { + a + } else { + panic!("Wrong variant") + }; + assert_eq!( + variant.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::F::a"), + KeyComposer::from_str("Contract::a") + ) + ); + Ok(()) + }) + .unwrap() +} diff --git a/crates/lang/tests/ui/storage_item/pass/complex_non_packed_struct.rs b/crates/lang/tests/ui/storage_item/pass/complex_non_packed_struct.rs new file mode 100644 index 00000000000..5ff3fac8523 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/complex_non_packed_struct.rs @@ -0,0 +1,88 @@ +use ink_prelude::vec::Vec; +use ink_primitives::KeyComposer; +use ink_storage::{ + traits::{ + AutoKey, + KeyHolder, + }, + Lazy, + Mapping, +}; + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Packed { + a: u8, + b: u16, + c: u32, + d: u64, + e: u128, + f: String, +} + +#[ink_lang::storage_item] +#[derive(Default)] +struct NonPacked { + a: Mapping, + b: Lazy, + c: Lazy, + d: Lazy>, +} + +#[ink_lang::storage_item] +#[derive(Default)] +struct Contract { + a: Lazy, + b: Mapping, + c: (Packed, Packed), +} + +fn main() { + ink_env::test::run_test::(|_| { + let contract = Contract::default(); + assert_eq!(contract.key(), 0); + + // contract.b + assert_eq!(contract.b.key(), KeyComposer::from_str("Contract::b")); + + // contract.a + assert_eq!(contract.a.key(), KeyComposer::from_str("Contract::a")); + + assert_eq!( + contract.a.get_or_default().a.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::a"), + KeyComposer::from_str("Contract::a") + ), + ); + + assert_eq!( + contract.a.get_or_default().b.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::b"), + KeyComposer::from_str("Contract::a") + ), + ); + + assert_eq!( + contract.a.get_or_default().c.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::c"), + KeyComposer::from_str("Contract::a") + ), + ); + + assert_eq!( + contract.a.get_or_default().d.key(), + KeyComposer::concat( + KeyComposer::from_str("NonPacked::d"), + KeyComposer::from_str("Contract::a") + ), + ); + Ok(()) + }) + .unwrap() +} diff --git a/crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs b/crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs new file mode 100644 index 00000000000..9ddd1f8e17b --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs @@ -0,0 +1,59 @@ +use ink_prelude::{ + collections::{ + BTreeMap, + BTreeSet, + }, + vec::Vec, +}; +use ink_storage::traits::Storable; + +#[derive(Default, PartialEq, Eq, PartialOrd, Ord, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +enum Deep2 { + #[default] + None, + A(u8), + B(u16), + C(u32), + D(u64), + E(u128), + F(String), + G { + a: u8, + b: String, + }, + H((u16, u32)), +} + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +enum Deep1 { + #[default] + None, + A(Deep2), + B((Deep2, Deep2)), + C(Vec), + D(BTreeMap), + E(BTreeSet), +} + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Contract { + a: Deep1, + b: Deep2, + c: (Deep1, Deep2), +} + +fn main() { + let _: Result = Storable::decode(&mut &[][..]); +} diff --git a/crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs b/crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs new file mode 100644 index 00000000000..fbaba78312e --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs @@ -0,0 +1,50 @@ +use ink_prelude::{ + collections::{ + BTreeMap, + BTreeSet, + }, + vec::Vec, +}; +use ink_storage::traits::Storable; + +#[derive(Default, PartialEq, Eq, PartialOrd, Ord, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Deep2 { + a: u8, + b: u16, + c: u32, + d: u64, + e: u128, + f: String, +} + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Deep1 { + a: Deep2, + b: (Deep2, Deep2), + c: Vec, + d: BTreeMap, + e: BTreeSet, +} + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Contract { + a: Deep1, + b: Deep2, + c: (Deep1, Deep2), +} + +fn main() { + let _: Result = Storable::decode(&mut &[][..]); +} diff --git a/crates/lang/tests/ui/storage_item/pass/default_storage_key_1.rs b/crates/lang/tests/ui/storage_item/pass/default_storage_key_1.rs new file mode 100644 index 00000000000..b518454e3de --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/default_storage_key_1.rs @@ -0,0 +1,15 @@ +use ink_storage::traits::{ + KeyHolder, + ManualKey, +}; + +#[ink_lang::storage_item] +struct Contract> { + a: u16, + b: u16, + c: u16, +} + +fn main() { + assert_eq!(::KEY, 123); +} diff --git a/crates/lang/tests/ui/storage_item/pass/default_storage_key_2.rs b/crates/lang/tests/ui/storage_item/pass/default_storage_key_2.rs new file mode 100644 index 00000000000..f49120bef50 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/default_storage_key_2.rs @@ -0,0 +1,15 @@ +use ink_storage::{ + traits, + traits::ManualKey, +}; + +#[ink_lang::storage_item] +struct Contract> { + a: u16, + b: u16, + c: u16, +} + +fn main() { + assert_eq!(::KEY, 123); +} diff --git a/crates/lang/tests/ui/storage_item/pass/default_storage_key_3.rs b/crates/lang/tests/ui/storage_item/pass/default_storage_key_3.rs new file mode 100644 index 00000000000..3ce45400535 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/default_storage_key_3.rs @@ -0,0 +1,12 @@ +use ink_storage::traits::ManualKey; + +#[ink_lang::storage_item] +struct Contract> { + a: u16, + b: u16, + c: u16, +} + +fn main() { + assert_eq!(::KEY, 123); +} diff --git a/crates/lang/tests/ui/storage_item/pass/default_storage_key_4.rs b/crates/lang/tests/ui/storage_item/pass/default_storage_key_4.rs new file mode 100644 index 00000000000..1b3bb886386 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/default_storage_key_4.rs @@ -0,0 +1,12 @@ +use ink_storage::traits::ManualKey; + +#[ink_lang::storage_item] +struct Contract> { + a: u16, + b: u16, + c: u16, +} + +fn main() { + assert_eq!(::KEY, 123); +} diff --git a/crates/lang/tests/ui/storage_item/pass/non_packed_tuple_struct.rs b/crates/lang/tests/ui/storage_item/pass/non_packed_tuple_struct.rs new file mode 100644 index 00000000000..f9d1bd761dd --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/non_packed_tuple_struct.rs @@ -0,0 +1,22 @@ +use ink_primitives::KeyComposer; +use ink_storage::{ + traits::KeyHolder, + Lazy, + Mapping, +}; + +#[ink_lang::storage_item] +#[derive(Default)] +struct Contract(Mapping, Lazy); + +fn main() { + ink_env::test::run_test::(|_| { + let contract = Contract::default(); + assert_eq!(contract.key(), 0); + + assert_eq!(contract.0.key(), KeyComposer::from_str("Contract::0")); + assert_eq!(contract.1.key(), KeyComposer::from_str("Contract::1")); + Ok(()) + }) + .unwrap() +} diff --git a/crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs b/crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs new file mode 100644 index 00000000000..7d265a835fd --- /dev/null +++ b/crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs @@ -0,0 +1,12 @@ +use ink_storage::traits::Storable; + +#[derive(Default, scale::Encode, scale::Decode)] +#[cfg_attr( + feature = "std", + derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +)] +struct Contract(String, u128); + +fn main() { + let _: Result = Storable::decode(&mut &[][..]); +} diff --git a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr index 073ed0e683b..635afb419e1 100644 --- a/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr +++ b/crates/lang/tests/ui/trait_def/fail/message_output_non_codec.stderr @@ -34,7 +34,7 @@ error[E0599]: the method `fire` exists for struct `CallBuilder>, = note: the following trait bounds were not satisfied: `NonCodec: parity_scale_codec::Decode` note: the following trait must be implemented - --> $CARGO/parity-scale-codec-3.1.5/src/codec.rs + --> $CARGO/parity-scale-codec-3.1.2/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/metadata/src/layout/mod.rs b/crates/metadata/src/layout/mod.rs index 48a16bba694..00ce1387b28 100644 --- a/crates/metadata/src/layout/mod.rs +++ b/crates/metadata/src/layout/mod.rs @@ -14,6 +14,10 @@ #[cfg(test)] mod tests; +mod validate; + +use core::fmt::Display; +pub use validate::ValidateLayout; use crate::{ serde_hex, @@ -54,16 +58,14 @@ pub enum Layout { /// /// This is the only leaf node within the layout graph. /// All layout nodes have this node type as their leafs. - /// - /// This represents the encoding of a single cell mapped to a single key. - Cell(CellLayout), + Leaf(CellLayout), + /// The root cell defines the storage key for all sub-trees. + Root(RootLayout), /// A layout that hashes values into the entire storage key space. /// /// This is commonly used by ink! hashmaps and similar data structures. Hash(HashLayout), - /// An array of associated storage cells encoded with a given type. - /// - /// This can also represent only a single cell. + /// An array of type associated with storage cell. Array(ArrayLayout), /// A struct layout with fields of different types. Struct(StructLayout), @@ -72,9 +74,9 @@ pub enum Layout { } /// A pointer into some storage region. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, From)] pub struct LayoutKey { - key: [u8; 32], + key: Key, } impl serde::Serialize for LayoutKey { @@ -82,7 +84,7 @@ impl serde::Serialize for LayoutKey { where S: serde::Serializer, { - serde_hex::serialize(&self.key, serializer) + serde_hex::serialize(&self.key.to_be_bytes(), serializer) } } @@ -91,28 +93,74 @@ impl<'de> serde::Deserialize<'de> for LayoutKey { where D: serde::Deserializer<'de>, { - let mut arr = [0; 32]; + let mut arr = [0; 4]; serde_hex::deserialize_check_len(d, serde_hex::ExpectedLen::Exact(&mut arr[..]))?; - Ok(arr.into()) + Ok(Key::from_be_bytes(arr).into()) } } impl<'a> From<&'a Key> for LayoutKey { fn from(key: &'a Key) -> Self { - Self { key: *key.as_ref() } + Self { key: *key } } } -impl From for LayoutKey { - fn from(key: Key) -> Self { - Self { key: *key.as_ref() } +impl LayoutKey { + /// Returns the key of the layout key. + pub fn key(&self) -> &Key { + &self.key } } -impl LayoutKey { - /// Returns the underlying bytes of the layout key. - pub fn to_bytes(&self) -> &[u8] { - &self.key +/// Sub-tree root. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, From, Serialize, Deserialize)] +#[serde(bound( + serialize = "F::Type: Serialize, F::String: Serialize", + deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned" +))] +pub struct RootLayout { + /// The root key of the sub-tree. + root_key: LayoutKey, + /// The storage layout of the unbounded layout elements. + layout: Box>, +} + +impl RootLayout { + /// Creates a new root layout. + pub fn new(root_key: LayoutKey, layout: L) -> Self + where + L: Into, + { + Self { + root_key, + layout: Box::new(layout.into()), + } + } +} + +impl IntoPortable for RootLayout { + type Output = RootLayout; + + fn into_portable(self, registry: &mut Registry) -> Self::Output { + RootLayout { + root_key: self.root_key, + layout: Box::new(self.layout.into_portable(registry)), + } + } +} + +impl RootLayout +where + F: Form, +{ + /// Returns the root key of the sub-tree. + pub fn root_key(&self) -> &LayoutKey { + &self.root_key + } + + /// Returns the storage layout of the unbounded layout elements. + pub fn layout(&self) -> &Layout { + &self.layout } } @@ -158,8 +206,11 @@ impl IntoPortable for Layout { fn into_portable(self, registry: &mut Registry) -> Self::Output { match self { - Layout::Cell(encoded_cell) => { - Layout::Cell(encoded_cell.into_portable(registry)) + Layout::Leaf(encoded_cell) => { + Layout::Leaf(encoded_cell.into_portable(registry)) + } + Layout::Root(encoded_cell) => { + Layout::Root(encoded_cell.into_portable(registry)) } Layout::Hash(hash_layout) => { Layout::Hash(hash_layout.into_portable(registry)) @@ -330,15 +381,13 @@ pub struct ArrayLayout { offset: LayoutKey, /// The number of elements in the array layout. len: u32, - /// The number of cells each element in the array layout consists of. - cells_per_elem: u64, /// The layout of the elements stored in the array layout. layout: Box>, } impl ArrayLayout { /// Creates an array layout with the given length. - pub fn new(at: K, len: u32, cells_per_elem: u64, layout: L) -> Self + pub fn new(at: K, len: u32, layout: L) -> Self where K: Into, L: Into, @@ -346,7 +395,6 @@ impl ArrayLayout { Self { offset: at.into(), len, - cells_per_elem, layout: Box::new(layout.into()), } } @@ -369,11 +417,6 @@ where self.len } - /// Returns the number of cells each element in the array layout consists of. - pub fn cells_per_elem(&self) -> u64 { - self.cells_per_elem - } - /// Returns the layout of the elements stored in the array layout. pub fn layout(&self) -> &Layout { &self.layout @@ -387,7 +430,6 @@ impl IntoPortable for ArrayLayout { ArrayLayout { offset: self.offset, len: self.len, - cells_per_elem: self.cells_per_elem, layout: Box::new(self.layout.into_portable(registry)), } } @@ -400,17 +442,21 @@ impl IntoPortable for ArrayLayout { deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned" ))] pub struct StructLayout { + /// The name of the struct. + name: F::String, /// The fields of the struct layout. fields: Vec>, } impl StructLayout { /// Creates a new struct layout. - pub fn new(fields: F) -> Self + pub fn new(name: N, fields: F) -> Self where + N: Into<&'static str>, F: IntoIterator, { Self { + name: name.into(), fields: fields.into_iter().collect(), } } @@ -420,6 +466,10 @@ impl StructLayout where F: Form, { + /// Returns the name of the struct. + pub fn name(&self) -> &F::String { + &self.name + } /// Returns the fields of the struct layout. pub fn fields(&self) -> &[FieldLayout] { &self.fields @@ -431,6 +481,7 @@ impl IntoPortable for StructLayout { fn into_portable(self, registry: &mut Registry) -> Self::Output { StructLayout { + name: self.name.into(), fields: self .fields .into_iter() @@ -448,9 +499,7 @@ impl IntoPortable for StructLayout { ))] pub struct FieldLayout { /// The name of the field. - /// - /// Can be missing, e.g. in case of an enum tuple struct variant. - name: Option, + name: F::String, /// The kind of the field. /// /// This is either a direct layout bound @@ -462,7 +511,7 @@ impl FieldLayout { /// Creates a new field layout. pub fn new(name: N, layout: L) -> Self where - N: Into>, + N: Into<&'static str>, L: Into, { Self { @@ -477,10 +526,8 @@ where F: Form, { /// Returns the name of the field. - /// - /// Can be missing, e.g. in case of an enum tuple struct variant. - pub fn name(&self) -> Option<&F::String> { - self.name.as_ref() + pub fn name(&self) -> &F::String { + &self.name } /// Returns the kind of the field. @@ -497,7 +544,7 @@ impl IntoPortable for FieldLayout { fn into_portable(self, registry: &mut Registry) -> Self::Output { FieldLayout { - name: self.name.map(|name| name.into_portable(registry)), + name: self.name.into_portable(registry), layout: self.layout.into_portable(registry), } } @@ -528,6 +575,8 @@ impl Discriminant { ))] #[serde(rename_all = "camelCase")] pub struct EnumLayout { + /// The name of the Enum. + name: F::String, /// The key where the discriminant is stored to dispatch the variants. dispatch_key: LayoutKey, /// The variants of the enum. @@ -536,12 +585,14 @@ pub struct EnumLayout { impl EnumLayout { /// Creates a new enum layout. - pub fn new(dispatch_key: K, variants: V) -> Self + pub fn new(name: N, dispatch_key: K, variants: V) -> Self where + N: Into<&'static str>, K: Into, V: IntoIterator, { Self { + name: name.into(), dispatch_key: dispatch_key.into(), variants: variants.into_iter().collect(), } @@ -552,6 +603,11 @@ impl EnumLayout where F: Form, { + /// Returns the name of the field. + pub fn name(&self) -> &F::String { + &self.name + } + /// Returns the key where the discriminant is stored to dispatch the variants. pub fn dispatch_key(&self) -> &LayoutKey { &self.dispatch_key @@ -568,6 +624,7 @@ impl IntoPortable for EnumLayout { fn into_portable(self, registry: &mut Registry) -> Self::Output { EnumLayout { + name: self.name.into(), dispatch_key: self.dispatch_key, variants: self .variants @@ -579,3 +636,52 @@ impl IntoPortable for EnumLayout { } } } + +/// An error that can occur during ink! metadata generation. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MetadataError { + /// Storage keys of two types intersect + ConflictKey(String, String), +} + +impl Display for MetadataError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{}", self.to_human_string()) + } +} + +impl MetadataError { + /// Returns a string representation of the error. + #[inline] + fn to_human_string(&self) -> String { + match self { + Self::ConflictKey(prev_path, curr_path) => { + format!( + "conflict storage key occurred for `{}`. \ + The same storage key is occupied by the `{}`.", + curr_path, + if prev_path.is_empty() { + "contract storage" + } else { + prev_path + } + ) + } + } + } +} + +#[test] +fn valid_error_message() { + assert_eq!( + MetadataError::ConflictKey("".to_string(), "Contract.c:".to_string()).to_string(), + "conflict storage key occurred for `Contract.c:`. \ + The same storage key is occupied by the `contract storage`." + ); + assert_eq!( + MetadataError::ConflictKey("Contract.a:".to_string(), "Contract.c:".to_string()) + .to_string(), + "conflict storage key occurred for `Contract.c:`. \ + The same storage key is occupied by the `Contract.a:`." + ) +} diff --git a/crates/metadata/src/layout/tests.rs b/crates/metadata/src/layout/tests.rs index c24dddc1ddf..e71567b35da 100644 --- a/crates/metadata/src/layout/tests.rs +++ b/crates/metadata/src/layout/tests.rs @@ -13,35 +13,29 @@ // limitations under the License. use super::*; -use ink_primitives::KeyPtr; +use ink_primitives::Key; #[test] fn layout_key_works() { - let layout_key = LayoutKey::from(Key::from([0x01; 32])); + let layout_key = LayoutKey::from(&1); let json = serde_json::to_string(&layout_key).unwrap(); - assert_eq!( - json, - "\"0x0101010101010101010101010101010101010101010101010101010101010101\"", - ); + assert_eq!(json, "\"0x00000001\"",); } -fn named_fields_struct_layout(key_ptr: &mut KeyPtr) -> Layout { - StructLayout::new(vec![ - FieldLayout::new( - "a", - CellLayout::new::(LayoutKey::from(key_ptr.advance_by(1))), - ), - FieldLayout::new( - "b", - CellLayout::new::(LayoutKey::from(key_ptr.advance_by(1))), - ), - ]) +fn named_fields_struct_layout(key: &Key) -> Layout { + StructLayout::new( + "Struct", + vec![ + FieldLayout::new("a", CellLayout::new::(LayoutKey::from(key))), + FieldLayout::new("b", CellLayout::new::(LayoutKey::from(key))), + ], + ) .into() } #[test] fn named_fields_work() { - let layout = named_fields_struct_layout(&mut KeyPtr::from(Key::from([0x00; 32]))); + let layout = named_fields_struct_layout(&345); let mut registry = Registry::new(); let compacted = layout.into_portable(&mut registry); let json = serde_json::to_value(&compacted).unwrap(); @@ -51,12 +45,8 @@ fn named_fields_work() { "fields": [ { "layout": { - "cell": { - "key": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x00000159", "ty": 0, } }, @@ -64,41 +54,35 @@ fn named_fields_work() { }, { "layout": { - "cell": { - "key": "0x\ - 0100000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x00000159", "ty": 1, } }, "name": "b", } - ] + ], + "name": "Struct", } } }; assert_eq!(json, expected); } -fn tuple_struct_layout(key_ptr: &mut KeyPtr) -> Layout { - StructLayout::new(vec![ - FieldLayout::new( - None, - CellLayout::new::(LayoutKey::from(key_ptr.advance_by(1))), - ), - FieldLayout::new( - None, - CellLayout::new::(LayoutKey::from(key_ptr.advance_by(1))), - ), - ]) +fn tuple_struct_layout(key: &Key) -> Layout { + StructLayout::new( + "(A, B)", + vec![ + FieldLayout::new("0", CellLayout::new::(LayoutKey::from(key))), + FieldLayout::new("1", CellLayout::new::(LayoutKey::from(key))), + ], + ) .into() } #[test] fn tuple_struct_work() { - let layout = tuple_struct_layout(&mut KeyPtr::from(Key::from([0x00; 32]))); + let layout = tuple_struct_layout(&234); let mut registry = Registry::new(); let compacted = layout.into_portable(&mut registry); let json = serde_json::to_value(&compacted).unwrap(); @@ -108,44 +92,38 @@ fn tuple_struct_work() { "fields": [ { "layout": { - "cell": { - "key": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000000ea", "ty": 0, } }, - "name": null, + "name": "0", }, { "layout": { - "cell": { - "key": "0x\ - 0100000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000000ea", "ty": 1, } }, - "name": null, + "name": "1", } - ] + ], + "name": "(A, B)", } } }; assert_eq!(json, expected); } -fn clike_enum_layout(key_ptr: &mut KeyPtr) -> Layout { +fn clike_enum_layout(key: &Key) -> Layout { EnumLayout::new( - key_ptr.advance_by(1), + "Enum", + key, vec![ - (Discriminant(0), StructLayout::new(vec![])), - (Discriminant(1), StructLayout::new(vec![])), - (Discriminant(2), StructLayout::new(vec![])), + (Discriminant(0), StructLayout::new("Struct0", vec![])), + (Discriminant(1), StructLayout::new("Struct1", vec![])), + (Discriminant(2), StructLayout::new("Struct2", vec![])), ], ) .into() @@ -153,27 +131,27 @@ fn clike_enum_layout(key_ptr: &mut KeyPtr) -> Layout { #[test] fn clike_enum_work() { - let layout = clike_enum_layout(&mut KeyPtr::from(Key::from([0x00; 32]))); + let layout = clike_enum_layout(&123); let mut registry = Registry::new(); let compacted = layout.into_portable(&mut registry); let json = serde_json::to_value(&compacted).unwrap(); let expected = serde_json::json! { { "enum": { - "dispatchKey": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "dispatchKey": "0x0000007b", + "name": "Enum", "variants": { "0": { "fields": [], + "name": "Struct0", }, "1": { "fields": [], + "name": "Struct1", }, "2": { "fields": [], + "name": "Struct2", }, } } @@ -182,49 +160,48 @@ fn clike_enum_work() { assert_eq!(json, expected); } -fn mixed_enum_layout(key_ptr: &mut KeyPtr) -> Layout { +fn mixed_enum_layout(key: &Key) -> Layout { EnumLayout::new( - *key_ptr.advance_by(1), + "Enum", + *key, vec![ - (Discriminant(0), StructLayout::new(vec![])), + (Discriminant(0), StructLayout::new("Struct0", vec![])), { - let mut variant_key_ptr = *key_ptr; + let variant_key = key; ( Discriminant(1), - StructLayout::new(vec![ - FieldLayout::new( - None, - CellLayout::new::(LayoutKey::from( - variant_key_ptr.advance_by(1), - )), - ), - FieldLayout::new( - None, - CellLayout::new::(LayoutKey::from( - variant_key_ptr.advance_by(1), - )), - ), - ]), + StructLayout::new( + "Struct1", + vec![ + FieldLayout::new( + "0", + CellLayout::new::(LayoutKey::from(variant_key)), + ), + FieldLayout::new( + "1", + CellLayout::new::(LayoutKey::from(variant_key)), + ), + ], + ), ) }, { - let mut variant_key_ptr = *key_ptr; + let variant_key = key; ( Discriminant(2), - StructLayout::new(vec![ - FieldLayout::new( - "a", - CellLayout::new::(LayoutKey::from( - variant_key_ptr.advance_by(1), - )), - ), - FieldLayout::new( - "b", - CellLayout::new::(LayoutKey::from( - variant_key_ptr.advance_by(1), - )), - ), - ]), + StructLayout::new( + "Struct2", + vec![ + FieldLayout::new( + "a", + CellLayout::new::(LayoutKey::from(variant_key)), + ), + FieldLayout::new( + "b", + CellLayout::new::(LayoutKey::from(variant_key)), + ), + ], + ), ) }, ], @@ -234,62 +211,49 @@ fn mixed_enum_layout(key_ptr: &mut KeyPtr) -> Layout { #[test] fn mixed_enum_work() { - let layout = mixed_enum_layout(&mut KeyPtr::from(Key::from([0x00; 32]))); + let layout = mixed_enum_layout(&456); let mut registry = Registry::new(); let compacted = layout.into_portable(&mut registry); let json = serde_json::to_value(&compacted).unwrap(); let expected = serde_json::json! { { "enum": { - "dispatchKey": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "dispatchKey": "0x000001c8", + "name": "Enum", "variants": { "0": { "fields": [], + "name": "Struct0", }, "1": { "fields": [ { "layout": { - "cell": { - "key": "0x\ - 0100000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000001c8", "ty": 0, } }, - "name": null, + "name": "0", }, { "layout": { - "cell": { - "key": "0x\ - 0200000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000001c8", "ty": 1, } }, - "name": null, + "name": "1", } ], + "name": "Struct1", }, "2": { "fields": [ { "layout": { - "cell": { - "key": "0x\ - 0100000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000001c8", "ty": 0, } }, @@ -297,18 +261,15 @@ fn mixed_enum_work() { }, { "layout": { - "cell": { - "key": "0x\ - 0200000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x000001c8", "ty": 1, } }, "name": "b", } ], + "name": "Struct2", }, } } @@ -317,8 +278,8 @@ fn mixed_enum_work() { assert_eq!(json, expected); } -fn unbounded_hashing_layout(key_ptr: &mut KeyPtr) -> Layout { - let root_key = key_ptr.advance_by(1); +fn unbounded_hashing_layout(key: &Key) -> Layout { + let root_key = key; HashLayout::new( root_key, HashingStrategy::new( @@ -333,7 +294,7 @@ fn unbounded_hashing_layout(key_ptr: &mut KeyPtr) -> Layout { #[test] fn unbounded_layout_works() { - let layout = unbounded_hashing_layout(&mut KeyPtr::from(Key::from([0x00; 32]))); + let layout = unbounded_hashing_layout(&567); let mut registry = Registry::new(); let compacted = layout.into_portable(&mut registry); let json = serde_json::to_value(&compacted).unwrap(); @@ -341,20 +302,12 @@ fn unbounded_layout_works() { { "hash": { "layout": { - "cell": { - "key": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "leaf": { + "key": "0x00000237", "ty": 0 } }, - "offset": "0x\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000\ - 0000000000000000", + "offset": "0x00000237", "strategy": { "hasher": "Blake2x256", "prefix": "0x696e6b2073746f7261676520686173686d6170", diff --git a/crates/metadata/src/layout/validate.rs b/crates/metadata/src/layout/validate.rs new file mode 100644 index 00000000000..2d11254df81 --- /dev/null +++ b/crates/metadata/src/layout/validate.rs @@ -0,0 +1,298 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::layout::{ + Layout, + MetadataError, + StructLayout, +}; +use ink_prelude::collections::HashMap; +use ink_primitives::Key; +use scale_info::form::MetaForm; + +/// It validates that the storage layout doesn't have conflicting storage keys. +/// Otherwise an error with a description of the conflict is returned. +pub struct ValidateLayout { + first_entry: HashMap, + name_stack: Vec, +} + +impl ValidateLayout { + /// Validates the storage layout. + pub fn validate(layout: &Layout) -> Result<(), MetadataError> { + let mut validator = Self { + first_entry: Default::default(), + name_stack: Default::default(), + }; + validator.recursive_validate(layout) + } + + fn recursive_validate( + &mut self, + layout: &Layout, + ) -> Result<(), MetadataError> { + match layout { + Layout::Root(root) => { + self.check_key(root.root_key.key())?; + self.recursive_validate(root.layout()) + } + Layout::Hash(hash) => self.recursive_validate(hash.layout()), + Layout::Array(array) => self.recursive_validate(array.layout()), + Layout::Struct(st) => self.check_struct_layout(st), + Layout::Enum(en) => { + // After `Enum::` we will have the struct -> `Enum::Struct` + self.name_stack.push(format!("{}::", en.name())); + for variant in en.variants().values() { + self.check_struct_layout(variant)?; + } + self.name_stack.pop().unwrap(); + Ok(()) + } + _ => Ok(()), + } + } + + fn check_struct_layout(&mut self, st: &StructLayout) -> Result<(), MetadataError> { + self.name_stack.push(st.name().to_string()); + for layout in st.fields() { + let name = layout.name(); + // After `Struct` we always have fields -> `Struct.field` + // After field we have `Struct` or `Enum` -> `Struct.field:Struct` + self.name_stack.push(format!(".{}:", name)); + + self.recursive_validate(layout.layout())?; + + self.name_stack.pop().unwrap(); + } + self.name_stack.pop().unwrap(); + Ok(()) + } + + fn check_key(&mut self, key: &Key) -> Result<(), MetadataError> { + let path = self.name_stack.join(""); + if let Some(prev_path) = self.first_entry.get(key) { + Err(MetadataError::ConflictKey(prev_path.clone(), path)) + } else { + self.first_entry.insert(*key, path); + Ok(()) + } + } +} + +#[cfg(test)] +mod tests { + use crate::layout::{ + CellLayout, + EnumLayout, + FieldLayout, + Layout, + MetadataError, + RootLayout, + StructLayout, + ValidateLayout, + }; + use ink_primitives::Key; + use std::collections::BTreeSet; + + #[test] + fn valid_layout_tree_only_roots() { + // Root(0) -> Root(1) -> Root(2) -> u32 + let layout = RootLayout::new( + 0.into(), + RootLayout::new( + 1.into(), + RootLayout::new(2.into(), CellLayout::new::(2.into())), + ), + ); + + assert!(ValidateLayout::validate(&Layout::Root(layout)).is_ok()) + } + + // If any of the root key are equal it should cause an error + fn valid_big_layout_tree( + key_for_root_0: Key, + key_for_root_1: Key, + key_for_root_2: Key, + key_for_root_3: Key, + key_for_root_4: Key, + ) -> Result<(), MetadataError> { + let root_0 = key_for_root_0.into(); + let root_1 = key_for_root_1.into(); + let root_2 = key_for_root_2.into(); + let root_3 = key_for_root_3.into(); + let root_4 = key_for_root_4.into(); + // Below the description of the layout tree. Inside `(...)` the expected storage key. + // Root(0) + // | + // Contract(0) + // / | \ + // a:Root(1) b:u32(0) c:Struct0(0) + // | / \ + // Vec(1) d:u128(0) f:Root(2) + // | + // Enum(2) + // / | \ + // First Second Third + // 0.Struct1 0.u8(2) 0.Root(3) + // | | + // Root(4) String + // | + // g:BTreeSet(4) + let layout = RootLayout::new( + root_0, + StructLayout::new( + "Contract", + vec![ + FieldLayout::new( + "a", + StructLayout::new( + "Struct0", + vec![ + FieldLayout::new("d", CellLayout::new::(root_0)), + FieldLayout::new( + "f", + RootLayout::new( + root_2, + EnumLayout::new( + "Enum", + root_2, + vec![ + ( + 0.into(), + StructLayout::new( + "First", + vec![FieldLayout::new( + "0", + StructLayout::new( + "Struct1", + vec![FieldLayout::new( + "g", + RootLayout::new( + root_4, + CellLayout::new::< + BTreeSet, + >( + root_4 + ), + ), + )], + ), + )], + ), + ), + ( + 1.into(), + StructLayout::new( + "Second", + vec![FieldLayout::new( + "0", + CellLayout::new::(root_2), + )], + ), + ), + ( + 2.into(), + StructLayout::new( + "Third", + vec![FieldLayout::new( + "0", + RootLayout::new( + root_3, + CellLayout::new::( + root_3, + ), + ), + )], + ), + ), + ], + ), + ), + ), + ], + ), + ), + FieldLayout::new("b", CellLayout::new::(root_0)), + FieldLayout::new( + "c", + RootLayout::new(root_1, CellLayout::new::>(root_1)), + ), + ], + ), + ); + + ValidateLayout::validate(&Layout::Root(layout)) + } + + #[test] + fn tree_is_valid() { + assert_eq!(Ok(()), valid_big_layout_tree(0, 1, 2, 3, 4)); + assert_eq!(Ok(()), valid_big_layout_tree(4, 3, 2, 1, 0)); + } + + #[test] + fn conflict_0_and_1() { + assert_eq!( + Err(MetadataError::ConflictKey( + "".to_string(), + "Contract.c:".to_string() + )), + valid_big_layout_tree(0, 0, 2, 3, 4) + ) + } + + #[test] + fn conflict_0_and_2() { + assert_eq!( + Err(MetadataError::ConflictKey( + "".to_string(), + "Contract.a:Struct0.f:".to_string() + )), + valid_big_layout_tree(0, 1, 0, 3, 4) + ) + } + + #[test] + fn conflict_0_and_3() { + assert_eq!( + Err(MetadataError::ConflictKey( + "".to_string(), + "Contract.a:Struct0.f:Enum::Third.0:".to_string() + )), + valid_big_layout_tree(0, 1, 2, 0, 4) + ) + } + + #[test] + fn conflict_0_and_4() { + assert_eq!( + Err(MetadataError::ConflictKey( + "".to_string(), + "Contract.a:Struct0.f:Enum::First.0:Struct1.g:".to_string() + )), + valid_big_layout_tree(0, 1, 2, 3, 0) + ) + } + + #[test] + fn conflict_3_and_4() { + assert_eq!( + Err(MetadataError::ConflictKey( + "Contract.a:Struct0.f:Enum::First.0:Struct1.g:".to_string(), + "Contract.a:Struct0.f:Enum::Third.0:".to_string() + )), + valid_big_layout_tree(0, 1, 2, 3, 3) + ) + } +} diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index bf5ad2b01f5..f40d65a56e6 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -18,10 +18,7 @@ include = ["/Cargo.toml", "src/**/*.rs", "/README.md", "/LICENSE"] ink_prelude = { version = "4.0.0", path = "../prelude/", default-features = false } scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "full"] } scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } -cfg-if = "1" - -[dev-dependencies] -criterion = "0.3.1" +sha2-const = { version = "0.1.2", default-features = false } [features] default = ["std"] @@ -29,9 +26,4 @@ std = [ "ink_prelude/std", "scale/std", "scale-info/std", -] - -[[bench]] -name = "bench" -path = "benches/bench.rs" -harness = false +] \ No newline at end of file diff --git a/crates/primitives/benches/bench.rs b/crates/primitives/benches/bench.rs deleted file mode 100644 index 8b2ecf69895..00000000000 --- a/crates/primitives/benches/bench.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use criterion::{ - black_box, - criterion_group, - criterion_main, - Criterion, -}; -use ink_primitives::{ - Key, - KeyPtr, -}; - -criterion_group!( - bench_key, - bench_key_add_assign_u64, - bench_key_add_assign_u64_one_ofvl, - bench_key_add_assign_u64_two_ofvls, - bench_key_add_assign_u64_three_ofvls, - bench_key_add_assign_u64_wrap, -); -criterion_group!( - bench_key_ptr, - bench_key_ptr_advance_by, - bench_key_ptr_advance_by_repeat, -); -criterion_main!(bench_key, bench_key_ptr); - -fn bench_key_add_assign_u64(c: &mut Criterion) { - let key = Key::from([0x00; 32]); - c.bench_function("Key2::add_assign(u64)", |b| { - b.iter(|| { - let mut copy = black_box(key); - let _ = black_box(|| copy += 1u64); - }) - }); -} - -fn bench_key_add_assign_u64_one_ofvl(c: &mut Criterion) { - let key = Key::from([ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - c.bench_function("Key2::add_assign(u64) - 1 ofvl", |b| { - b.iter(|| { - let mut copy = black_box(key); - let _ = black_box(|| copy += 1u64); - }) - }); -} - -fn bench_key_add_assign_u64_two_ofvls(c: &mut Criterion) { - let key = Key::from([ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - c.bench_function("Key2::add_assign(u64) - 2 ofvls", |b| { - b.iter(|| { - let mut copy = black_box(key); - let _ = black_box(|| copy += 1u64); - }) - }); -} - -fn bench_key_add_assign_u64_three_ofvls(c: &mut Criterion) { - let key = Key::from([ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]); - c.bench_function("Key2::add_assign(u64) - 3 ofvls", |b| { - b.iter(|| { - let mut copy = black_box(key); - let _ = black_box(|| copy += 1u64); - }) - }); -} - -fn bench_key_add_assign_u64_wrap(c: &mut Criterion) { - let key = Key::from([0xFF; 32]); - c.bench_function("Key2::add_assign(u64) - wrap", |b| { - b.iter(|| { - let mut copy = black_box(key); - let _ = black_box(|| copy += 1u64); - }) - }); -} - -fn bench_key_ptr_advance_by(c: &mut Criterion) { - let key = Key::from([0x00; 32]); - c.bench_function("KeyPtr2::advance_by copy", |b| { - b.iter(|| { - let mut key_ptr = KeyPtr::from(key); - let _ = black_box(key_ptr.advance_by(1)); - }) - }); -} - -fn bench_key_ptr_advance_by_repeat(c: &mut Criterion) { - let key = Key::from([0x00; 32]); - let mut key_ptr = KeyPtr::from(key); - c.bench_function("KeyPtr2::advance_by reuse", |b| { - b.iter(|| { - let _ = black_box(key_ptr.advance_by(1)); - }) - }); -} diff --git a/crates/primitives/src/key.rs b/crates/primitives/src/key.rs index 61fde0ba7ca..a4e84843d28 100644 --- a/crates/primitives/src/key.rs +++ b/crates/primitives/src/key.rs @@ -12,341 +12,69 @@ // See the License for the specific language governing permissions and // limitations under the License. -use cfg_if::cfg_if; -use core::{ - fmt::{ - self, - Debug, - Display, - Formatter, - }, - ops::AddAssign, -}; -#[cfg(feature = "std")] -use scale_info::{ - build::Fields, - Path, - Type, - TypeInfo, -}; - -/// A key into the smart contract storage. -/// -/// # Note -/// -/// - The storage of an ink! smart contract can be viewed as a key-value store. -/// - In order to manipulate its storage an ink! smart contract is required -/// to indicate the respective cells using this primitive type. -/// - The `Key` type can be compared to a raw pointer and also allows operations -/// similar to pointer arithmetic. -/// - Users usually should not have to deal with this low-level primitive themselves -/// and instead use the more high-level primitives provided by the `ink_storage` -/// crate. -#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct Key([u8; 32]); - -impl Key { - /// Creates a new key instance from the given bytes. - /// - /// # Note - /// - /// This constructor only exists since it is not yet possible to define - /// the `From` trait implementation as const. - #[inline] - pub const fn new(bytes: [u8; 32]) -> Self { - Self(bytes) - } -} - -impl From<[u8; 32]> for Key { - #[inline] - fn from(bytes: [u8; 32]) -> Self { - Self::new(bytes) - } -} - -impl AsRef<[u8; 32]> for Key { - #[inline] - fn as_ref(&self) -> &[u8; 32] { - &self.0 - } -} - -impl AsMut<[u8; 32]> for Key { - #[inline] - fn as_mut(&mut self) -> &mut [u8; 32] { - &mut self.0 - } -} - -impl Key { - fn write_bytes(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "0x")?; - let bytes = self.as_ref(); - let len_bytes = bytes.len(); - let len_chunk = 4; - let len_chunks = len_bytes / len_chunk; - for i in 0..len_chunks { - let offset = i * len_chunk; - write!( - f, - "_{:02X}{:02X}{:02X}{:02X}", - bytes[offset], - bytes[offset + 1], - bytes[offset + 2], - bytes[offset + 3] - )?; +use ink_prelude::vec; +use sha2_const::Sha256; + +pub type Key = u32; + +/// Contains all rules related to storage key creation. +pub struct KeyComposer; + +impl KeyComposer { + /// Concatenate two `Key` into one. If one of the keys is zero, then return another + /// without hashing. If both keys are non-zero, return the hash of both keys. + pub const fn concat(left: Key, right: Key) -> Key { + match (left, right) { + (0, 0) => 0, + (0, _) => right, + (_, 0) => left, + (left, right) => { + let hash = Sha256::new() + .update(&left.to_be_bytes()) + .update(&right.to_be_bytes()) + .finalize(); + Key::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) + } } - Ok(()) } -} -impl Debug for Key { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "Key(")?; - self.write_bytes(f)?; - write!(f, ")")?; - Ok(()) + /// Return the storage key from the supplied `str`. + pub const fn from_str(str: &str) -> Key { + Self::from_bytes(str.as_bytes()) } -} -impl Display for Key { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.write_bytes(f) + /// Returns the storage key from the supplied `bytes`. + pub const fn from_bytes(bytes: &[u8]) -> Key { + let hash = Sha256::new().update(bytes).finalize(); + Key::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) } -} - -impl Key { - /// Reinterprets the underlying bytes of the key as `&[u64; 4]`. - /// - /// # Safety - /// - /// This is only safe to do on little-endian systems therefore - /// this function is only enabled on these platforms. - #[cfg(target_endian = "little")] - fn reinterpret_as_u64x4(&self) -> &[u64; 4] { - // SAFETY: Conversion is only safe on little endian architectures. - unsafe { &*(&self.0 as *const [u8; 32] as *const [u64; 4]) } - } - - /// Reinterprets the underlying bytes of the key as `&mut [u64; 4]`. - /// - /// # Safety - /// - /// This is only safe to do on little-endian systems therefore - /// this function is only enabled on these platforms. - #[cfg(target_endian = "little")] - fn reinterpret_as_u64x4_mut(&mut self) -> &mut [u64; 4] { - // SAFETY: Conversion is only safe on little endian architectures. - unsafe { &mut *(&mut self.0 as *mut [u8; 32] as *mut [u64; 4]) } - } -} - -impl scale::Encode for Key { - #[inline] - fn size_hint(&self) -> usize { - 32 - } - - #[inline] - fn encode_to(&self, output: &mut O) - where - O: scale::Output + ?Sized, - { - output.write(self.as_ref()); - } - - #[inline] - fn using_encoded(&self, f: F) -> R - where - F: FnOnce(&[u8]) -> R, - { - f(self.as_ref()) - } - - #[inline] - fn encoded_size(&self) -> usize { - self.size_hint() - } -} - -impl scale::EncodeLike<[u8; 32]> for Key {} - -impl scale::Decode for Key { - #[inline] - fn decode(input: &mut I) -> Result - where - I: scale::Input, - { - let bytes = <[u8; 32] as scale::Decode>::decode(input)?; - Ok(Self::from(bytes)) - } - - #[inline] - fn encoded_fixed_size() -> Option { - Some(32) - } -} -#[cfg(feature = "std")] -impl TypeInfo for Key { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("Key", "ink_primitives")) - .composite( - Fields::unnamed().field(|f| f.ty::<[u8; 32]>().type_name("[u8; 32]")), - ) - } -} - -impl Key { - /// Adds the `u64` value to the `Key`. - /// - /// # Note - /// - /// This implementation is heavily optimized for little-endian Wasm platforms. - /// - /// # Developer Note - /// - /// Since we are operating on little-endian we can convert the underlying `[u8; 32]` - /// array to `[u64; 4]`. Since in WebAssembly `u64` is supported natively unlike `u8` - /// it is more efficient to work on chunks of `u8` represented as `u64`. - #[cfg(target_endian = "little")] - fn add_assign_u64_le(&mut self, rhs: u64) { - let words = self.reinterpret_as_u64x4_mut(); - let (res0, ovfl) = words[0].overflowing_add(rhs); - let (res1, ovfl) = words[1].overflowing_add(ovfl as u64); - let (res2, ovfl) = words[2].overflowing_add(ovfl as u64); - let (res3, _ovfl) = words[3].overflowing_add(ovfl as u64); - words[0] = res0; - words[1] = res1; - words[2] = res2; - words[3] = res3; - } - - /// Adds the `u64` value to the key storing the result in `result`. - /// - /// # Note - /// - /// This implementation is heavily optimized for little-endian Wasm platforms. - /// - /// # Developer Note - /// - /// Since we are operating on little-endian we can convert the underlying `[u8; 32]` - /// array to `[u64; 4]`. Since in WebAssembly `u64` is supported natively unlike `u8` - /// it is more efficient to work on chunks of `u8` represented as `u64`. - #[cfg(target_endian = "little")] - fn add_assign_u64_le_using(&self, rhs: u64, result: &mut Key) { - let input = self.reinterpret_as_u64x4(); - let result = result.reinterpret_as_u64x4_mut(); - let (res0, ovfl) = input[0].overflowing_add(rhs); - let (res1, ovfl) = input[1].overflowing_add(ovfl as u64); - let (res2, ovfl) = input[2].overflowing_add(ovfl as u64); - let (res3, _ovfl) = input[3].overflowing_add(ovfl as u64); - result[0] = res0; - result[1] = res1; - result[2] = res2; - result[3] = res3; - } - - /// Adds the `u64` value to the `Key`. - /// - /// # Note - /// - /// This is a fallback implementation that has not been optimized for any - /// specific target platform or endianness. - #[cfg(target_endian = "big")] - fn add_assign_u64_be(&mut self, rhs: u64) { - let rhs_bytes = rhs.to_be_bytes(); - let lhs_bytes = self.as_mut(); - let len_rhs = rhs_bytes.len(); - let len_lhs = lhs_bytes.len(); - let mut carry = 0; - for i in 0..len_rhs { - let (res, ovfl) = - lhs_bytes[i].overflowing_add(rhs_bytes[i].wrapping_add(carry)); - lhs_bytes[i] = res; - carry = ovfl as u8; - } - for i in len_rhs..len_lhs { - let (res, ovfl) = lhs_bytes[i].overflowing_add(carry); - lhs_bytes[i] = res; - carry = ovfl as u8; - if carry == 0 { - return - } - } - } - - /// Adds the `u64` value to the key storing the result in `result`. - /// - /// # Note - /// - /// This is a fallback implementation that has not been optimized for any - /// specific target platform or endianness. - #[cfg(target_endian = "big")] - fn add_assign_u64_be_using(&self, rhs: u64, result: &mut Key) { - let rhs_bytes = rhs.to_be_bytes(); - let lhs_bytes = self.as_ref(); - let result_bytes = result.as_mut(); - let len_rhs = rhs_bytes.len(); - let len_lhs = lhs_bytes.len(); - let mut carry = 0; - for i in 0..len_rhs { - let (res, ovfl) = - lhs_bytes[i].overflowing_add(rhs_bytes[i].wrapping_add(carry)); - result_bytes[i] = res; - carry = ovfl as u8; - } - for i in len_rhs..len_lhs { - let (res, ovfl) = lhs_bytes[i].overflowing_add(carry); - result_bytes[i] = res; - carry = ovfl as u8; - // Note: We cannot bail out early in this case in order to - // guarantee that we fully overwrite the result key. - } - } - - /// Adds the `u64` value to the key storing the result in `result`. - /// /// # Note /// - /// This will overwrite the contents of the `result` key. - #[inline] - pub fn add_assign_using(&self, rhs: T, result: &mut Key) - where - T: Into, - { - let rhs = rhs.into(); - cfg_if! { - if #[cfg(target_endian = "little")] { - self.add_assign_u64_le_using(rhs, result); - } else { - self.add_assign_u64_be_using(rhs, result); - } - } - } -} - -impl AddAssign for Key { - #[inline] - fn add_assign(&mut self, rhs: u64) { - cfg_if! { - if #[cfg(target_endian = "little")] { - self.add_assign_u64_le(rhs); - } else { - self.add_assign_u64_be(rhs); - } - } - } -} - -impl AddAssign<&u64> for Key { - #[inline] - fn add_assign(&mut self, rhs: &u64) { - >::add_assign(self, *rhs) + /// - `variant_name` is `None` for structures and unions. + /// - if the field is unnamed then `field_name` is `"{}"` where `{}` is a number of the field. + /// + /// Evaluates the storage key of the field in the structure, variant or union. + /// + /// 1. Compute the ASCII byte representation of `struct_name` and call it `S`. + /// 1. If `variant_name` is `Some` then computes the ASCII byte representation and call it `V`. + /// 1. Compute the ASCII byte representation of `field_name` and call it `F`. + /// 1. Concatenate (`S` and `F`) or (`S`, `V` and `F`) using `::` as separator and call it `C`. + /// 1. Apply the `SHA2` 256-bit hash `H` of `C`. + /// 1. The first 4 bytes of `H` make up the storage key. + pub fn compute_key(struct_name: &str, variant_name: &str, field_name: &str) -> u32 { + let separator = &b"::"[..]; + let composed_key = if !variant_name.is_empty() { + vec![ + struct_name.as_bytes(), + variant_name.as_bytes(), + field_name.as_bytes(), + ] + .join(separator) + } else { + vec![struct_name.as_bytes(), field_name.as_bytes()].join(separator) + }; + + Self::from_bytes(composed_key.as_slice()) } } diff --git a/crates/primitives/src/key_ptr.rs b/crates/primitives/src/key_ptr.rs deleted file mode 100644 index 449a9b385db..00000000000 --- a/crates/primitives/src/key_ptr.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::Key; - -/// A key pointer. -/// -/// This wraps a base key and provides an interface to mimic pointer arithmetic. -/// Mainly used to coordinate keys through static storage structures. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct KeyPtr { - /// The underlying offset key. - key: Key, - /// The last shift performed. - last_shift: u64, -} - -impl From for KeyPtr { - #[inline] - fn from(key: Key) -> Self { - Self { key, last_shift: 0 } - } -} - -impl KeyPtr { - /// Advances the key pointer by the given amount and returns the old value. - #[inline] - pub fn advance_by(&mut self, new_shift: u64) -> &Key { - let old_shift = core::mem::replace(&mut self.last_shift, new_shift); - self.key += old_shift; - &self.key - } - - /// Returns the underlying offset key. - pub fn key(&self) -> &Key { - &self.key - } -} diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 84005cfbdbe..3dcb1e5f452 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -24,12 +24,8 @@ #![cfg_attr(not(feature = "std"), no_std)] mod key; -mod key_ptr; -#[cfg(test)] -mod tests; - -pub use self::{ - key::Key, - key_ptr::KeyPtr, +pub use self::key::{ + Key, + KeyComposer, }; diff --git a/crates/primitives/src/tests.rs b/crates/primitives/src/tests.rs deleted file mode 100644 index d353aab467c..00000000000 --- a/crates/primitives/src/tests.rs +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::Key; - -const TEST_BYTES: [u8; 32] = *b"\ - \x00\x01\x02\x03\x04\x05\x06\x07\ - \x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\ - \x10\x11\x12\x13\x14\x15\x16\x17\ - \x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\ - "; - -mod key { - use super::*; - use core::ops::AddAssign; - use scale::{ - Decode, - Encode, - }; - - #[test] - fn default_works() { - let mut default_key = ::default(); - assert_eq!(default_key, Key::from([0x00_u8; 32])); - assert_eq!(default_key.as_ref(), &[0x00_u8; 32]); - assert_eq!(default_key.as_mut(), &mut [0x00_u8; 32]); - } - - #[test] - fn debug_works() { - let key = Key::from(TEST_BYTES); - assert_eq!( - format!("{:?}", key), - String::from( - "Key(0x\ - _00010203_04050607\ - _08090A0B_0C0D0E0F\ - _10111213_14151617\ - _18191A1B_1C1D1E1F\ - )" - ), - ); - } - - #[test] - fn display_works() { - let key = Key::from(TEST_BYTES); - assert_eq!( - format!("{}", key), - String::from( - "0x\ - _00010203_04050607\ - _08090A0B_0C0D0E0F\ - _10111213_14151617\ - _18191A1B_1C1D1E1F" - ), - ); - } - - #[test] - fn from_works() { - let mut bytes = TEST_BYTES; - assert_eq!(Key::from(TEST_BYTES).as_ref(), &bytes); - assert_eq!(Key::from(TEST_BYTES).as_mut(), &mut bytes); - } - - #[test] - fn encode_decode_works() { - let key = Key::from(TEST_BYTES); - let encoded = key.encode(); - let decoded = Key::decode(&mut &encoded[..]).unwrap(); - assert_eq!(key, decoded); - } - - #[test] - fn encode_works() { - let bytes = TEST_BYTES; - let encoded = Key::from(bytes).encode(); - assert_eq!(encoded, bytes); - } - - #[test] - fn decode_works() { - let bytes = TEST_BYTES; - let decoded = Key::decode(&mut &bytes[..]).unwrap(); - assert_eq!(decoded, Key::from(bytes)); - } - - #[test] - fn codec_hints_work() { - let key = Key::default(); - assert_eq!(key.size_hint(), 32); - assert_eq!(key.encoded_size(), 32); - assert_eq!(Key::encoded_fixed_size(), Some(32)); - } - - #[test] - fn add_assign_one_to_zero_works() { - let bytes = [0x00; 32]; - let expected = { - let mut bytes = [0x00; 32]; - bytes[0] = 0x01; - bytes - }; - let mut key = Key::from(bytes); - key.add_assign(1u64); - assert_eq!(key.as_ref(), &expected); - } - - #[test] - fn add_assign_using_one_to_zero_works() { - let bytes = [0x00; 32]; - let expected = { - let mut bytes = [0x00; 32]; - bytes[0] = 0x01; - bytes - }; - let input = Key::from(bytes); - let mut result = Key::default(); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } - - const OVERFLOW_1_TEST_BYTES: [u8; 32] = *b"\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - "; - - #[test] - fn add_assign_with_ovfl_1_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[8] = 0x01; - expected - }; - let mut key = Key::from(OVERFLOW_1_TEST_BYTES); - key.add_assign(1u64); - assert_eq!(key.as_ref(), &expected); - } - - #[test] - fn add_assign_using_with_ovfl_1_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[8] = 0x01; - expected - }; - let input = Key::from(OVERFLOW_1_TEST_BYTES); - let mut result = Key::default(); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } - - const OVERFLOW_2_TEST_BYTES: [u8; 32] = *b"\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - "; - - #[test] - fn add_assign_with_ovfl_2_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[16] = 0x01; - expected - }; - let mut key = Key::from(OVERFLOW_2_TEST_BYTES); - key.add_assign(1u64); - assert_eq!(key.as_ref(), &expected); - } - - #[test] - fn add_assign_using_with_ovfl_2_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[16] = 0x01; - expected - }; - let input = Key::from(OVERFLOW_2_TEST_BYTES); - let mut result = Key::default(); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } - - const OVERFLOW_3_TEST_BYTES: [u8; 32] = *b"\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\ - \x00\x00\x00\x00\x00\x00\x00\x00\ - "; - - #[test] - fn add_assign_with_ovfl_3_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[24] = 0x01; - expected - }; - let mut key = Key::from(OVERFLOW_3_TEST_BYTES); - key.add_assign(1u64); - assert_eq!(key.as_ref(), &expected); - } - - #[test] - fn add_assign_using_with_ovfl_3_works() { - let expected = { - let mut expected = [0x00; 32]; - expected[24] = 0x01; - expected - }; - let input = Key::from(OVERFLOW_3_TEST_BYTES); - let mut result = Key::default(); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } - - #[test] - fn add_assign_with_wrap_works() { - const BYTES: [u8; 32] = [0xFF; 32]; - let expected = [0x00; 32]; - let mut key = Key::from(BYTES); - key.add_assign(1u64); - assert_eq!(key.as_ref(), &expected); - } - - #[test] - fn add_assign_using_with_wrap_works() { - const BYTES: [u8; 32] = [0xFF; 32]; - let expected = [0x00; 32]; - let input = Key::from(BYTES); - let mut result = Key::default(); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } - - #[test] - fn add_assign_to_zero_works() { - const TEST_VALUES: &[u64] = &[0, 1, 42, 10_000, u32::MAX as u64, u64::MAX]; - for test_value in TEST_VALUES { - let mut key = ::default(); - let expected = { - let mut expected = [0x00; 32]; - expected[0..8].copy_from_slice(&test_value.to_le_bytes()); - expected - }; - key += test_value; - assert_eq!(key.as_ref(), &expected); - } - } - - #[test] - fn add_assign_using_to_zero_works() { - const TEST_VALUES: &[u64] = &[0, 1, 42, 10_000, u32::MAX as u64, u64::MAX]; - let zero = ::default(); - for test_value in TEST_VALUES { - let expected = { - let mut expected = [0x00; 32]; - expected[0..8].copy_from_slice(&test_value.to_le_bytes()); - expected - }; - let mut result = Key::default(); - zero.add_assign_using(*test_value, &mut result); - assert_eq!(result.as_ref(), &expected); - } - } - - #[test] - fn add_assign_using_override_works() { - let bytes = [0x00; 32]; - let expected = { - let mut bytes = [0x00; 32]; - bytes[0] = 0x01; - bytes - }; - let input = Key::from(bytes); - let mut result = Key::from([0xFF; 32]); - input.add_assign_using(1u64, &mut result); - assert_eq!(result.as_ref(), &expected); - } -} diff --git a/crates/storage/Cargo.toml b/crates/storage/Cargo.toml index 3b7f38ee281..dc6f34c1f65 100644 --- a/crates/storage/Cargo.toml +++ b/crates/storage/Cargo.toml @@ -26,6 +26,7 @@ derive_more = { version = "0.99", default-features = false, features = ["from", scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } cfg-if = "1.0" array-init = { version = "2.0", default-features = false } +const_format = "0.2.23" [dev-dependencies] quickcheck = "1.0" diff --git a/crates/storage/codegen/Cargo.toml b/crates/storage/codegen/Cargo.toml new file mode 100644 index 00000000000..02b063fb8ac --- /dev/null +++ b/crates/storage/codegen/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "ink_storage_codegen" +version = "4.0.0" +authors = ["Parity Technologies "] +edition = "2021" + +license = "Apache-2.0" +readme = "../README.md" +repository = "https://github.com/paritytech/ink" +documentation = "https://docs.rs/ink_storage_derive" +homepage = "https://www.parity.io/" +description = "[ink!] Utils codegen crate related to ink_storage." +keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"] +categories = ["no-std", "embedded"] +include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] + +[dependencies] +ink_primitives = { version = "4.0.0", path = "../../primitives", default-features = false } +syn = { version = "1", features = ["full"] } \ No newline at end of file diff --git a/crates/storage/codegen/LICENSE b/crates/storage/codegen/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/crates/storage/codegen/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/crates/storage/codegen/README.md b/crates/storage/codegen/README.md new file mode 120000 index 00000000000..8a33348c7d8 --- /dev/null +++ b/crates/storage/codegen/README.md @@ -0,0 +1 @@ +../../../README.md \ No newline at end of file diff --git a/crates/storage/codegen/src/lib.rs b/crates/storage/codegen/src/lib.rs new file mode 100644 index 00000000000..9c82e7fc641 --- /dev/null +++ b/crates/storage/codegen/src/lib.rs @@ -0,0 +1,80 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashSet; +use syn::Data; + +/// Provides common methods for `DeriveInput`. +/// +/// **Note:** This is only for internal usage in the `codegen` module. +pub trait DeriveUtils { + /// Finds the salt of the structure, enum or union. + /// The salt is any generic that has bound `KeyHolder`. + fn find_salt(&self) -> Option; + + /// Return all types of the input. + fn all_types(&self) -> Vec; +} + +impl DeriveUtils for syn::DeriveInput { + fn find_salt(&self) -> Option { + self.generics.params.iter().find_map(|param| { + if let syn::GenericParam::Type(type_param) = param { + if type_param.bounds.len() == 1 { + let bound = type_param.bounds.first().unwrap(); + if let syn::TypeParamBound::Trait(trait_bound) = bound { + let segments = &trait_bound.path.segments; + if !segments.is_empty() + && segments.last().unwrap().ident == "KeyHolder" + { + return Some(type_param.clone()) + } + } + } + } + None + }) + } + + fn all_types(&self) -> Vec { + let res: Vec<_> = match self.data.clone() { + Data::Struct(st) => st.fields.iter().map(|field| field.ty.clone()).collect(), + Data::Enum(en) => { + en.variants + .iter() + .flat_map(|variant| variant.fields.iter()) + .map(|field| field.ty.clone()) + .collect() + } + Data::Union(un) => { + un.fields + .named + .iter() + .map(|field| field.ty.clone()) + .collect() + } + }; + let mut set = HashSet::new(); + res.into_iter() + .filter(|ty| { + if !set.contains(ty) { + set.insert(ty.clone()); + true + } else { + false + } + }) + .collect() + } +} diff --git a/crates/storage/derive/Cargo.toml b/crates/storage/derive/Cargo.toml index ac4ee160589..e8e688ca25e 100644 --- a/crates/storage/derive/Cargo.toml +++ b/crates/storage/derive/Cargo.toml @@ -22,11 +22,12 @@ quote = "1" syn = { version = "1", features = ["full"] } proc-macro2 = "1" synstructure = "0.12.4" +ink_storage_codegen = { version = "4.0.0", path = "../codegen" } [dev-dependencies] scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "full"] } ink_env = { version = "4.0.0", path = "../../env" } ink_primitives = { version = "4.0.0", path = "../../primitives" } ink_metadata = { version = "4.0.0", path = "../../metadata" } -ink_prelude = { version = "4.0.0", path = "../../prelude/" } +ink_prelude = { version = "4.0.0", path = "../../prelude" } ink_storage = { version = "4.0.0", path = ".." } diff --git a/crates/storage/derive/src/item.rs b/crates/storage/derive/src/item.rs new file mode 100644 index 00000000000..13a369300ef --- /dev/null +++ b/crates/storage/derive/src/item.rs @@ -0,0 +1,87 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ink_storage_codegen::DeriveUtils; +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + format_ident, + quote, + ToTokens, +}; +use syn::{ + parse2, + GenericParam, +}; + +fn item_inner(s: synstructure::Structure) -> TokenStream2 { + let ident = s.ast().ident.clone(); + let salt_ident = format_ident!("__ink_generic_salt"); + + let mut generics = s.ast().generics.clone(); + generics + .params + .push(parse2(quote! { #salt_ident : ::ink_storage::traits::KeyHolder }).unwrap()); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + let (_, ty_generics_original, _) = s.ast().generics.split_for_impl(); + + if s.ast().find_salt().is_some() { + let inner_salt_ident = s.ast().find_salt().unwrap().ident.to_token_stream(); + let ty_generics: Vec<_> = s + .ast() + .generics + .params + .clone() + .into_iter() + .map(|param| { + let ident = match param { + GenericParam::Type(t) => t.ident.to_token_stream(), + GenericParam::Lifetime(l) => l.lifetime.to_token_stream(), + GenericParam::Const(c) => c.ident.to_token_stream(), + }; + if inner_salt_ident.to_string() == ident.to_string() { + Some(quote! { + #salt_ident + }) + } else { + Some(ident) + } + }) + .collect(); + + quote! { + impl #impl_generics ::ink_storage::traits::Item<#salt_ident> for #ident #ty_generics_original #where_clause { + type Type = #ident <#(#ty_generics),*>; + type PreferredKey = #inner_salt_ident; + } + } + } else { + quote! { + impl #impl_generics ::ink_storage::traits::Item<#salt_ident> for #ident #ty_generics_original #where_clause { + type Type = #ident #ty_generics_original; + type PreferredKey = ::ink_storage::traits::AutoKey; + } + } + } +} + +pub fn item_derive(s: synstructure::Structure) -> TokenStream2 { + let derive = item_inner(s); + + quote! { + const _ : () = { + #derive + }; + } +} diff --git a/crates/storage/derive/src/key_holder.rs b/crates/storage/derive/src/key_holder.rs new file mode 100644 index 00000000000..106950477e1 --- /dev/null +++ b/crates/storage/derive/src/key_holder.rs @@ -0,0 +1,37 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ink_storage_codegen::DeriveUtils; +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + ToTokens, +}; + +pub fn key_holder_derive(mut s: synstructure::Structure) -> TokenStream2 { + s.add_bounds(synstructure::AddBounds::None) + .underscore_const(true); + + let salt = if let Some(param) = s.ast().find_salt() { + param.ident.to_token_stream() + } else { + quote! { () } + }; + + s.gen_impl(quote! { + gen impl ::ink_storage::traits::KeyHolder for @Self { + const KEY: ::ink_primitives::Key = <#salt as ::ink_storage::traits::KeyHolder>::KEY; + } + }) +} diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index 96f43ddf17e..f48954afede 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -15,163 +15,115 @@ //! Custom derive for `ink_storage` traits. //! //! This crate provides helpers to define your very own custom storage data -//! structures that work along the `ink_storage` data structures by implementing -//! `SpreadLayout` and `PackedLayout` traits. -//! -//! See [Spread vs. Packed](https://ink.substrate.io/datastructures/spread-packed-layout) -//! for more details of these two root strategies. -//! -//! # Examples -//! -//! ```no_run -//! use ink_storage::traits::{SpreadLayout, push_spread_root}; -//! use ink_primitives::Key; -//! -//! # ink_env::test::run_test::(|_| { -//! // Enum -//! #[derive(SpreadLayout)] -//! enum Vote { -//! Yes, -//! No -//! } -//! -//! // Strucut -//! #[derive(SpreadLayout)] -//! struct NamedFields { -//! a: u32, -//! b: [u32; 32], -//! }; -//! -//! // Created a custom structure and told which key to update. -//! push_spread_root(&NamedFields{ a: 123, b: [22; 32] }, &mut Key::from([0x42; 32])); -//! # Ok(()) -//! # }); -//! ``` +//! structures that work along the `ink_storage` data structures. extern crate proc_macro; -mod packed_layout; -mod spread_allocate; -mod spread_layout; +mod item; +mod key_holder; +mod storable; mod storage_layout; #[cfg(test)] mod tests; use self::{ - packed_layout::packed_layout_derive, - spread_allocate::spread_allocate_derive, - spread_layout::spread_layout_derive, + item::item_derive, + key_holder::key_holder_derive, + storable::storable_derive, storage_layout::storage_layout_derive, }; synstructure::decl_derive!( - [SpreadLayout] => - /// Derives `ink_storage`'s `SpreadLayout` trait for the given `struct` or `enum`. + [Storable] => + /// Derives `ink_storage`'s `Storable` trait for the given `struct`, `enum` or `union`. /// /// # Examples /// /// ``` - /// use ink_primitives::Key; - /// use ink_storage::traits::{ - /// SpreadLayout, - /// push_spread_root, - /// pull_spread_root, - ///}; + /// use ink_storage::traits::Storable; /// - /// # ink_env::test::run_test::(|_| { - /// #[derive(SpreadLayout)] + /// #[derive(Storable)] /// struct NamedFields { /// a: u32, - /// b: [u32; 32], + /// b: [u32; 1], /// } /// - /// let value = NamedFields { - /// a: 123, - /// b: [22; 32], - /// }; - /// - /// push_spread_root(&value, &mut Key::from([0x42; 32])); - /// let value2: NamedFields = pull_spread_root(&mut Key::from([0x42; 32])); - /// assert_eq!(value.a, value2.a); - /// # Ok(()) - /// # }); + /// let value = ::decode(&mut &[123, 123][..]); /// ``` - spread_layout_derive + storable_derive ); synstructure::decl_derive!( - [PackedLayout] => - /// Derives `ink_storage`'s `PackedLayout` trait for the given `struct` or `enum`. + [Item] => + /// Derives `ink_storage`'s `Item` trait for the given `struct` or `enum`. /// /// # Examples /// /// ``` - /// use scale::{Encode, Decode}; - /// use ink_primitives::Key; /// use ink_storage::traits::{ - /// SpreadLayout, - /// PackedLayout, - /// push_packed_root, - /// pull_packed_root + /// Item, + /// KeyHolder, + /// AutoItem, + /// AutoKey, + /// ManualKey, + /// Storable, /// }; /// - /// # ink_env::test::run_test::(|_| { - /// #[derive(Encode, Decode, SpreadLayout, PackedLayout)] + /// #[derive(Default, Item, Storable)] /// struct NamedFields { /// a: u32, /// b: [u32; 32], /// } /// - /// let mut value = NamedFields { - /// a: 123, - /// b: [22; 32], - /// }; + /// let _: NamedFields = >::Type::default(); + /// let _: NamedFields = >>::Type::default(); + /// + /// #[derive(Item, KeyHolder, Storable)] + /// struct NamedFieldsStorage { + /// a: >>::Type, + /// b: <[u32; 32] as AutoItem>>::Type, + /// } /// - /// push_packed_root(&value, &mut Key::from([0x42; 32])); - /// let value2: NamedFields = pull_packed_root(&mut Key::from([0x42; 32])); - /// assert_eq!(value.a, value2.a); - /// # Ok(()) - /// # }); + /// // (AutoKey | ManualKey<123>) -> ManualKey<123> + /// assert_eq!(123, as AutoItem>>::Type::KEY); + /// // (ManualKey<321> | ManualKey<123>) -> ManualKey<321> + /// assert_eq!(321, > as AutoItem>>::Type::KEY); /// ``` - packed_layout_derive + item_derive ); - synstructure::decl_derive!( - [SpreadAllocate] => - /// Derives `ink_storage`'s `SpreadAllocate` trait for the given `struct`. - /// - /// # Note - /// - /// As of now `enum` types are not supported! + [KeyHolder] => + /// Derives `ink_storage`'s `KeyHolder` trait for the given `struct` or `enum`. /// /// # Examples /// /// ``` - /// use ink_primitives::Key; /// use ink_storage::traits::{ - /// SpreadAllocate, - /// SpreadLayout, - /// allocate_spread_root, - ///}; + /// AutoItem, + /// KeyHolder, + /// ManualKey, + /// AutoKey, + /// }; /// - /// #[derive(SpreadAllocate, SpreadLayout)] - /// # #[derive(Debug, PartialEq)] + /// #[derive(KeyHolder)] /// struct NamedFields { /// a: u32, /// b: [u32; 32], /// } /// - /// let allocated: NamedFields = allocate_spread_root(&Key::from([0x42; 32])); - /// assert_eq!( - /// allocated, - /// NamedFields { - /// a: 0, - /// b: [0; 32], - /// } - /// ); + /// assert_eq!(::KEY, 0); + /// + /// #[derive(KeyHolder)] + /// struct NamedFieldsManualKey { + /// a: >>::Type, + /// b: <[u32; 32] as AutoItem>>::Type, + /// } + /// + /// assert_eq!( as KeyHolder>::KEY, 0); + /// assert_eq!( as KeyHolder>::KEY, 0); + /// assert_eq!(> as KeyHolder>::KEY, 123); /// ``` - spread_allocate_derive + key_holder_derive ); - synstructure::decl_derive!( [StorageLayout] => /// Derives `ink_storage`'s `StorageLayout` trait for the given `struct` or `enum`. @@ -180,35 +132,24 @@ synstructure::decl_derive!( /// /// ``` /// use ink_metadata::layout::Layout::Struct; - /// use ink_primitives::Key; - /// use ink_storage::traits::{ - /// SpreadLayout, - /// StorageLayout, - /// push_spread_root, - /// KeyPtr, - /// }; + /// use ink_storage::traits::StorageLayout; /// - /// # ink_env::test::run_test::(|_| { - /// #[derive(SpreadLayout, StorageLayout)] + /// #[derive(StorageLayout)] /// struct NamedFields { /// a: u32, /// b: [u32; 32], /// } /// - /// let mut key = Key::from([0x42; 32]); + /// let key = 0x123; /// let mut value = NamedFields { /// a: 123, /// b: [22; 32], /// }; /// - /// push_spread_root(&value, &key); - /// - /// if let Struct(layout) = ::layout(&mut KeyPtr::from(key)) { - /// assert_eq!(*layout.fields()[0].name().unwrap(), "a"); - /// assert_eq!(*layout.fields()[1].name().unwrap(), "b"); + /// if let Struct(layout) = ::layout(&key) { + /// assert_eq!(*layout.fields()[0].name(), "a"); + /// assert_eq!(*layout.fields()[1].name(), "b"); /// } - /// # Ok(()) - /// # }); /// ``` storage_layout_derive ); diff --git a/crates/storage/derive/src/packed_layout.rs b/crates/storage/derive/src/packed_layout.rs deleted file mode 100644 index 388346e0179..00000000000 --- a/crates/storage/derive/src/packed_layout.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Derives `ink_storage`'s `PackedLayout` trait for the given `struct` or `enum`. -pub fn packed_layout_derive(mut s: synstructure::Structure) -> TokenStream2 { - s.bind_with(|_| synstructure::BindStyle::Move) - .add_bounds(synstructure::AddBounds::Generics) - .underscore_const(true); - let pull_body = s.each(|binding| { - quote! { ::ink_storage::traits::PackedLayout::pull_packed(#binding, __key); } - }); - let push_body = s.each(|binding| { - quote! { ::ink_storage::traits::PackedLayout::push_packed(#binding, __key); } - }); - let clear_body = s.each(|binding| { - quote! { ::ink_storage::traits::PackedLayout::clear_packed(#binding, __key); } - }); - s.gen_impl(quote! { - gen impl ::ink_storage::traits::PackedLayout for @Self { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { #pull_body } - } - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { #push_body } - } - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { #clear_body } - } - } - }) -} diff --git a/crates/storage/derive/src/spread_allocate.rs b/crates/storage/derive/src/spread_allocate.rs deleted file mode 100644 index 0dfadd4aebc..00000000000 --- a/crates/storage/derive/src/spread_allocate.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Derives `ink_storage`'s `SpreadAllocate` trait for the given type. -pub fn spread_allocate_derive(mut s: synstructure::Structure) -> TokenStream2 { - s.bind_with(|_| synstructure::BindStyle::Move) - .add_bounds(synstructure::AddBounds::Generics) - .underscore_const(true); - match s.ast().data { - syn::Data::Struct(_) => derive_struct(s), - syn::Data::Enum(_) => { - panic!("cannot derive `SpreadAllocate` for `enum` types") - } - syn::Data::Union(_) => { - panic!("cannot derive `SpreadAllocate` for `union` types") - } - } -} - -/// Derives `ink_storage`'s `SpreadAllocate` trait for the given `struct`. -fn derive_struct(s: synstructure::Structure) -> TokenStream2 { - assert!(s.variants().len() == 1, "can only operate on structs"); - let variant = &s.variants()[0]; - let allocate_body = variant.construct(|field, _index| { - let ty = &field.ty; - quote! { - <#ty as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr) - } - }); - s.gen_impl(quote! { - gen impl ::ink_storage::traits::SpreadAllocate for @Self { - fn allocate_spread(__key_ptr: &mut ::ink_primitives::KeyPtr) -> Self { - #allocate_body - } - } - }) -} diff --git a/crates/storage/derive/src/spread_layout.rs b/crates/storage/derive/src/spread_layout.rs deleted file mode 100644 index cefb39490b2..00000000000 --- a/crates/storage/derive/src/spread_layout.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use proc_macro2::TokenStream as TokenStream2; -use quote::quote; - -/// Generates the tokens to compute the maximum of the numbers given via -/// their token streams at compilation time. -/// -/// # Note -/// -/// Since Rust currently does not allow conditionals in const contexts -/// we use the array indexing trick to compute the maximum element: -/// -/// ```no_compile -/// max(a, b) = [a, b][(a < b) as usize] -/// ``` -fn max_n(args: &[TokenStream2]) -> TokenStream2 { - match args.split_first() { - Some((head, rest)) => { - let rest = max_n(rest); - quote! { - [#head, #rest][(#head < #rest) as ::core::primitive::usize] - } - } - None => quote! { 0u64 }, - } -} - -/// Generates the tokens for the `SpreadLayout` footprint of some type. -fn footprint(s: &synstructure::Structure) -> TokenStream2 { - let variant_footprints = s - .variants() - .iter() - .map(|variant| { - variant - .ast() - .fields - .iter() - .map(|field| &field.ty) - .map(|ty| quote! { <#ty as ::ink_storage::traits::SpreadLayout>::FOOTPRINT }) - .fold(quote! { 0u64 }, |lhs, rhs| { - quote! { (#lhs + #rhs) } - }) - }) - .collect::>(); - max_n(&variant_footprints[..]) -} - -/// Generates the tokens for the `SpreadLayout` `REQUIRES_DEEP_CLEAN_UP` constant for the given structure. -fn requires_deep_clean_up(s: &synstructure::Structure) -> TokenStream2 { - s.variants() - .iter() - .map(|variant| { - variant - .ast() - .fields - .iter() - .map(|field| &field.ty) - .map(|ty| quote! { <#ty as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP }) - .fold(quote! { false }, |lhs, rhs| { - quote! { (#lhs || #rhs) } - }) - }) - .fold(quote! { false }, |lhs, rhs| { - quote! { (#lhs || #rhs) } - }) -} - -/// `SpreadLayout` derive implementation for `struct` types. -fn spread_layout_struct_derive(s: &synstructure::Structure) -> TokenStream2 { - assert!(s.variants().len() == 1, "can only operate on structs"); - let footprint_body = footprint(s); - let requires_deep_clean_up_body = requires_deep_clean_up(s); - let variant: &synstructure::VariantInfo = &s.variants()[0]; - let pull_body = variant.construct(|field, _index| { - let ty = &field.ty; - quote! { - <#ty as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) - } - }); - let push_body = variant.each(|binding| { - quote! { - ::ink_storage::traits::SpreadLayout::push_spread(#binding, __key_ptr); - } - }); - let clear_body = s.each(|field| { - quote! { - ::ink_storage::traits::SpreadLayout::clear_spread(#field, __key_ptr); - } - }); - s.gen_impl(quote! { - gen impl ::ink_storage::traits::SpreadLayout for @Self { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = #footprint_body; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = #requires_deep_clean_up_body; - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - #pull_body - } - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { #push_body } - } - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { #clear_body } - } - } - }) -} - -/// `SpreadLayout` derive implementation for `enum` types. -fn spread_layout_enum_derive(s: &synstructure::Structure) -> TokenStream2 { - assert!( - !s.variants().is_empty(), - "encountered invalid empty enum type deriving SpreadLayout trait" - ); - let footprint_body = footprint(s); - let requires_deep_clean_up_body = requires_deep_clean_up(s); - let pull_body = s - .variants() - .iter() - .map(|variant| { - variant.construct(|field, _index| { - let ty = &field.ty; - quote! { - <#ty as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) - } - }) - }) - .enumerate() - .fold(quote! {}, |acc, (index, variant)| { - let index = index as u8; - quote! { - #acc - #index => #variant, - } - }); - - let push_body = s.variants().iter().enumerate().map(|(index, variant)| { - let pat = variant.pat(); - let index = index as u8; - let fields = variant.bindings().iter().map(|field| { - quote! { - ::ink_storage::traits::SpreadLayout::push_spread(#field, __key_ptr); - } - }); - quote! { - #pat => { - { <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread(&#index, __key_ptr); } - #( - { #fields } - )* - } - } - }); - let clear_body = s.each(|field| { - quote! { - ::ink_storage::traits::SpreadLayout::clear_spread(#field, __key_ptr); - } - }); - s.gen_impl(quote! { - gen impl ::ink_storage::traits::SpreadLayout for @Self { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = 1 + #footprint_body; - - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = #requires_deep_clean_up_body; - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - match <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) { - #pull_body - _ => unreachable!("encountered invalid enum discriminant"), - } - } - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - #( - #push_body - )* - } - } - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - #clear_body - } - } - } - }) -} - -/// Derives `ink_storage`'s `SpreadLayout` trait for the given `struct` or `enum`. -pub fn spread_layout_derive(mut s: synstructure::Structure) -> TokenStream2 { - s.bind_with(|_| synstructure::BindStyle::Move) - .add_bounds(synstructure::AddBounds::Generics) - .underscore_const(true); - match s.ast().data { - syn::Data::Struct(_) => spread_layout_struct_derive(&s), - syn::Data::Enum(_) => spread_layout_enum_derive(&s), - _ => { - panic!( - "cannot derive `SpreadLayout` or `PackedLayout` for Rust `union` items" - ) - } - } -} diff --git a/crates/storage/derive/src/storable.rs b/crates/storage/derive/src/storable.rs new file mode 100644 index 00000000000..0661ee1853b --- /dev/null +++ b/crates/storage/derive/src/storable.rs @@ -0,0 +1,140 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use proc_macro2::TokenStream as TokenStream2; +use quote::{ + quote, + quote_spanned, +}; +use syn::spanned::Spanned; + +/// `Storable` derive implementation for `struct` types. +fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 { + assert_eq!(s.variants().len(), 1, "can only operate on structs"); + let variant: &synstructure::VariantInfo = &s.variants()[0]; + let decode_body = variant.construct(|field, _index| { + let ty = &field.ty; + let span = ty.span(); + quote_spanned!(span => + <#ty as ::ink_storage::traits::Storable>::decode(__input)? + ) + }); + let encode_body = variant.each(|binding| { + let span = binding.ast().ty.span(); + quote_spanned!(span => + ::ink_storage::traits::Storable::encode(#binding, __dest); + ) + }); + + s.gen_impl(quote! { + gen impl ::ink_storage::traits::Storable for @Self { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok(#decode_body) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { #encode_body } + } + } + }) +} + +/// `Storable` derive implementation for `enum` types. +fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { + assert!( + !s.variants().is_empty(), + "encountered invalid empty enum type deriving Storable trait" + ); + let decode_body = s + .variants() + .iter() + .map(|variant| { + variant.construct(|field, _index| { + let ty = &field.ty; + let span = ty.span(); + quote_spanned!(span => + <#ty as ::ink_storage::traits::Storable>::decode(__input)? + ) + }) + }) + .enumerate() + .fold(quote! {}, |acc, (index, variant)| { + let index = index as u8; + quote! { + #acc + #index => #variant, + } + }); + + let encode_body = s.variants().iter().enumerate().map(|(index, variant)| { + let pat = variant.pat(); + let index = index as u8; + let fields = variant.bindings().iter().map(|field| { + let span = field.ast().ty.span(); + quote_spanned!(span => + ::ink_storage::traits::Storable::encode(#field, __dest); + ) + }); + quote! { + #pat => { + { <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&#index, __dest); } + #( + { #fields } + )* + } + } + }); + s.gen_impl(quote! { + gen impl ::ink_storage::traits::Storable for @Self { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + match <::core::primitive::u8 as ::ink_storage::traits::Storable>::decode(__input)? { + #decode_body + _ => unreachable!("encountered invalid enum discriminant"), + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + #( + #encode_body + )* + } + } + } + }) +} + +/// Derives `ink_storage`'s `Storable` trait for the given `struct` or `enum`. +pub fn storable_derive(mut s: synstructure::Structure) -> TokenStream2 { + s.bind_with(|_| synstructure::BindStyle::Move) + .add_bounds(synstructure::AddBounds::Fields) + .underscore_const(true); + match s.ast().data { + syn::Data::Struct(_) => storable_struct_derive(&s), + syn::Data::Enum(_) => storable_enum_derive(&s), + _ => { + panic!("cannot derive `Storable` for Rust `union` items") + } + } +} diff --git a/crates/storage/derive/src/storage_layout.rs b/crates/storage/derive/src/storage_layout.rs index ae2749d0ea6..5283b29d8f6 100644 --- a/crates/storage/derive/src/storage_layout.rs +++ b/crates/storage/derive/src/storage_layout.rs @@ -18,19 +18,22 @@ use quote::quote; fn field_layout<'a>( variant: &'a synstructure::VariantInfo, ) -> impl Iterator + 'a { - variant.ast().fields.iter().map(|field| { + variant.ast().fields.iter().enumerate().map(|(i, field)| { let ident = match field.ident.as_ref() { Some(ident) => { let ident_str = ident.to_string(); - quote! { ::core::option::Option::Some(#ident_str) } + quote! { #ident_str } + } + None => { + let index = i.to_string(); + quote! { #index } } - None => quote! { ::core::option::Option::None }, }; let ty = &field.ty; quote! { ::ink_metadata::layout::FieldLayout::new( #ident, - <#ty as ::ink_storage::traits::StorageLayout>::layout(__key_ptr), + <#ty as ::ink_storage::traits::StorageLayout>::layout(__key), ) } }) @@ -45,15 +48,19 @@ fn storage_layout_struct(s: &synstructure::Structure) -> TokenStream2 { s.variants().len() == 1, "structs must have at most one variant" ); + let struct_ident = s.ast().ident.clone(); let variant: &synstructure::VariantInfo = &s.variants()[0]; let field_layouts = field_layout(variant); s.gen_impl(quote! { gen impl ::ink_storage::traits::StorageLayout for @Self { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new([ - #(#field_layouts ,)* - ]) + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(#struct_ident), + [ + #(#field_layouts ,)* + ] + ) ) } } @@ -66,6 +73,7 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { "s must be an enum item" ); let variant_layouts = s.variants().iter().enumerate().map(|(n, variant)| { + let variant_ident = variant.ast().ident; let discriminant = variant .ast() .discriminant @@ -75,24 +83,26 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { let field_layouts = field_layout(variant); quote! { { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(#discriminant), - ::ink_metadata::layout::StructLayout::new([ - #(#field_layouts ,)* - ]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(#variant_ident), + [ + #(#field_layouts ,)* + ] + ), ) } } }); + let enum_ident = s.ast().ident.clone(); s.gen_impl(quote! { gen impl ::ink_storage::traits::StorageLayout for @Self { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { - let dispatch_key = __key_ptr.advance_by(1); + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( - ::ink_metadata::layout::LayoutKey::from(dispatch_key), + ::core::stringify!(#enum_ident), + ::ink_metadata::layout::LayoutKey::from(__key), [ #(#variant_layouts ,)* ] @@ -105,7 +115,7 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { pub fn storage_layout_derive(mut s: synstructure::Structure) -> TokenStream2 { s.bind_with(|_| synstructure::BindStyle::Move) - .add_bounds(synstructure::AddBounds::Generics) + .add_bounds(synstructure::AddBounds::Fields) .underscore_const(true); match s.ast().data { syn::Data::Struct(_) => storage_layout_struct(&s), diff --git a/crates/storage/derive/src/tests/item.rs b/crates/storage/derive/src/tests/item.rs new file mode 100644 index 00000000000..31bf49b9108 --- /dev/null +++ b/crates/storage/derive/src/tests/item.rs @@ -0,0 +1,160 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::item_derive; + +#[test] +fn unit_struct_works() { + crate::test_derive! { + item_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct + { + type Type = UnitStruct; + type PreferredKey = ::ink_storage::traits::AutoKey; + } + }; + } + no_build + } +} + +#[test] +fn unit_struct_salt_works() { + crate::test_derive! { + item_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl< + Salt: ::ink_storage::traits::KeyHolder, + __ink_generic_salt: ::ink_storage::traits::KeyHolder + > + ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct + { + type Type = UnitStruct<__ink_generic_salt>; + type PreferredKey = Salt; + } + }; + } + no_build + } +} + +#[test] +fn struct_works() { + crate::test_derive! { + item_derive { + struct NamedFields { + a: i32, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields + { + type Type = NamedFields; + type PreferredKey = ::ink_storage::traits::AutoKey; + } + }; + } + no_build + } +} + +#[test] +fn struct_salt_works() { + crate::test_derive! { + item_derive { + struct NamedFields { + a: i32, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl< + Salt: KeyHolder, + __ink_generic_salt: ::ink_storage::traits::KeyHolder + > + ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields + { + type Type = NamedFields<__ink_generic_salt>; + type PreferredKey = Salt; + } + }; + } + no_build + } +} + +#[test] +fn enum_works() { + crate::test_derive! { + item_derive { + enum MixedEnum { + A, + B(i32, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum + { + type Type = MixedEnum; + type PreferredKey = ::ink_storage::traits::AutoKey; + } + }; + } + no_build + } +} + +#[test] +fn enum_salt_works() { + crate::test_derive! { + item_derive { + enum MixedEnum { + A, + B(u32, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl< + Salt: traits::KeyHolder, + __ink_generic_salt: ::ink_storage::traits::KeyHolder + > + ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum + { + type Type = MixedEnum<__ink_generic_salt>; + type PreferredKey = Salt; + } + }; + } + no_build + } +} diff --git a/crates/storage/derive/src/tests/key_holder.rs b/crates/storage/derive/src/tests/key_holder.rs new file mode 100644 index 00000000000..a1575b0e8e6 --- /dev/null +++ b/crates/storage/derive/src/tests/key_holder.rs @@ -0,0 +1,192 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::key_holder_derive; + +#[test] +fn unit_struct_works() { + crate::test_derive! { + key_holder_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for UnitStruct { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn unit_struct_generic_works() { + crate::test_derive! { + key_holder_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for UnitStruct { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn unit_struct_salt_works() { + crate::test_derive! { + key_holder_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for UnitStruct { + const KEY: ::ink_primitives::Key = ::KEY; + } + }; + } + no_build + } +} + +#[test] +fn struct_works() { + crate::test_derive! { + key_holder_derive { + struct NamedFields { + a: i32, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for NamedFields { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn struct_generic_works() { + crate::test_derive! { + key_holder_derive { + struct NamedFields { + a: T, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for NamedFields { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn struct_salt_works() { + crate::test_derive! { + key_holder_derive { + struct NamedFields { + a: i32, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for NamedFields { + const KEY: ::ink_primitives::Key = ::KEY; + } + }; + } + no_build + } +} + +#[test] +fn enum_works() { + crate::test_derive! { + key_holder_derive { + enum MixedEnum { + A, + B(i32, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for MixedEnum { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn enum_generic_works() { + crate::test_derive! { + key_holder_derive { + enum MixedEnum { + A, + B(T, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for MixedEnum { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + } + }; + } + no_build + } +} + +#[test] +fn enum_salt_works() { + crate::test_derive! { + key_holder_derive { + enum MixedEnum { + A, + B(u32, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::KeyHolder for MixedEnum { + const KEY: ::ink_primitives::Key = ::KEY; + } + }; + } + no_build + } +} diff --git a/crates/storage/derive/src/tests/mod.rs b/crates/storage/derive/src/tests/mod.rs index 0e232816450..6d8e5814efc 100644 --- a/crates/storage/derive/src/tests/mod.rs +++ b/crates/storage/derive/src/tests/mod.rs @@ -12,7 +12,39 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod packed_layout; -mod spread_allocate; -mod spread_layout; +mod item; +mod key_holder; +mod storable; mod storage_layout; + +#[macro_export] +macro_rules! test_derive { + ($name:path { $($i:tt)* } expands to { $($o:tt)* }) => { + { + #[allow(dead_code)] + fn ensure_compiles() { + $($i)* + $($o)* + } + + $crate::test_derive!($name { $($i)* } expands to { $($o)* } no_build); + } + }; + + ($name:path { $($i:tt)* } expands to { $($o:tt)* } no_build) => { + { + let i = stringify!( $($i)* ); + let parsed = ::syn::parse_str::<::syn::DeriveInput>(i) + .expect(concat!( + "Failed to parse input to `#[derive(", + stringify!($name), + ")]`", + )); + + let res = $name(::synstructure::Structure::new(&parsed)); + + let expected = quote::quote!( $($o)* ); + assert_eq!(expected.to_string(), res.to_string()); + } + }; +} diff --git a/crates/storage/derive/src/tests/packed_layout.rs b/crates/storage/derive/src/tests/packed_layout.rs deleted file mode 100644 index 51881e45711..00000000000 --- a/crates/storage/derive/src/tests/packed_layout.rs +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::packed_layout_derive; - -#[test] -fn unit_struct_works() { - synstructure::test_derive! { - packed_layout_derive { - struct UnitStruct; - } - expands to { - const _: () = { - impl ::ink_storage::traits::PackedLayout for UnitStruct { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { - UnitStruct => {} - } - } - - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { - UnitStruct => {} - } - } - - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { - UnitStruct => {} - } - } - } - }; - } - no_build - } -} - -#[test] -fn struct_works() { - synstructure::test_derive! { - packed_layout_derive { - struct NamedFields { - a: i32, - b: [u8; 32], - d: Box, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::PackedLayout for NamedFields { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { - NamedFields { - a: __binding_0, - b: __binding_1, - d: __binding_2, - } => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_2, __key); - } - } - } - } - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { - NamedFields { - a: __binding_0, - b: __binding_1, - d: __binding_2, - } => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_2, __key); - } - } - } - } - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { - NamedFields { - a: __binding_0, - b: __binding_1, - d: __binding_2, - } => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_2, __key); - } - } - } - } - } - }; - } - no_build - } -} - -#[test] -fn enum_works() { - synstructure::test_derive! { - packed_layout_derive { - enum MixedEnum { - A, - B(i32, [u8; 32]), - C { a: i32, b: (bool, i32) }, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::PackedLayout for MixedEnum { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { - MixedEnum::A => {} - MixedEnum::B(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - } - MixedEnum::C { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - } - } - } - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { - MixedEnum::A => {} - MixedEnum::B(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - } - MixedEnum::C { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - } - } - } - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { - MixedEnum::A => {} - MixedEnum::B(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - } - MixedEnum::C { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - } - } - } - } - }; - } - no_build - } -} - -#[test] -fn generic_struct_works() { - synstructure::test_derive! { - packed_layout_derive { - struct GenericStruct { - a: T1, - b: (T1, T2), - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::PackedLayout for GenericStruct - where - T1: ::ink_storage::traits::PackedLayout, - T2: ::ink_storage::traits::PackedLayout - { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { - GenericStruct { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - } - } - } - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { - GenericStruct { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - } - } - } - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { - GenericStruct { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - } - } - } - } - }; - } - no_build - } -} - -#[test] -fn generic_enum_works() { - synstructure::test_derive! { - packed_layout_derive { - enum GenericEnum { - Tuple(T1, T2), - Named { a: T1, b: T2 }, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::PackedLayout for GenericEnum - where - T1: ::ink_storage::traits::PackedLayout, - T2: ::ink_storage::traits::PackedLayout - { - fn pull_packed(&mut self, __key: &::ink_primitives::Key) { - match self { - GenericEnum::Tuple(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - } - GenericEnum::Named { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::pull_packed(__binding_1, __key); - } - } - } - } - fn push_packed(&self, __key: &::ink_primitives::Key) { - match self { - GenericEnum::Tuple(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - } - GenericEnum::Named { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::push_packed(__binding_1, __key); - } - } - } - } - fn clear_packed(&self, __key: &::ink_primitives::Key) { - match self { - GenericEnum::Tuple(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - } - GenericEnum::Named { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_0, __key); - } - { - ::ink_storage::traits::PackedLayout::clear_packed(__binding_1, __key); - } - } - } - } - } - }; - } - no_build - } -} diff --git a/crates/storage/derive/src/tests/spread_allocate.rs b/crates/storage/derive/src/tests/spread_allocate.rs deleted file mode 100644 index 3970d907ee3..00000000000 --- a/crates/storage/derive/src/tests/spread_allocate.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::spread_allocate_derive; -use syn::parse_quote; - -#[test] -#[should_panic(expected = "cannot derive `SpreadAllocate` for `enum` types")] -fn enum_fails() { - spread_allocate_derive(synstructure::Structure::new(&parse_quote! { - enum Enum { A, B, C } - })); -} - -#[test] -#[should_panic(expected = "Unable to create synstructure::Structure: \ - Error(\"unexpected unsupported untagged union\")")] -fn union_fails() { - spread_allocate_derive(synstructure::Structure::new(&parse_quote! { - union Union { a: i32, b: u32 } - })); -} - -#[test] -fn unit_struct_works() { - synstructure::test_derive! { - spread_allocate_derive { - struct UnitStruct; - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadAllocate for UnitStruct { - fn allocate_spread(__key_ptr: &mut ::ink_primitives::KeyPtr) -> Self { - UnitStruct - } - } - }; - } - no_build - } -} - -#[test] -fn struct_works() { - synstructure::test_derive! { - spread_allocate_derive { - struct NamedFields { - a: i32, - b: [u8; 32], - c: Box, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadAllocate for NamedFields { - fn allocate_spread(__key_ptr: &mut ::ink_primitives::KeyPtr) -> Self { - NamedFields { - a: ::allocate_spread(__key_ptr), - b: <[u8; 32] as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr), - c: as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr), - } - } - } - }; - } - no_build - } -} - -#[test] -fn generic_struct_works() { - synstructure::test_derive! { - spread_allocate_derive { - struct GenericStruct { - a: T1, - b: (T1, T2), - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadAllocate for GenericStruct - where - T1: ::ink_storage::traits::SpreadAllocate, - T2: ::ink_storage::traits::SpreadAllocate - { - fn allocate_spread(__key_ptr: &mut ::ink_primitives::KeyPtr) -> Self { - GenericStruct { - a: ::allocate_spread(__key_ptr), - b: <(T1, T2) as ::ink_storage::traits::SpreadAllocate>::allocate_spread(__key_ptr), - } - } - } - }; - } - no_build - } -} diff --git a/crates/storage/derive/src/tests/spread_layout.rs b/crates/storage/derive/src/tests/spread_layout.rs deleted file mode 100644 index 1e5289bfe6f..00000000000 --- a/crates/storage/derive/src/tests/spread_layout.rs +++ /dev/null @@ -1,744 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// These tests are partly testing if code is expanded correctly. -// Hence the syntax contains a number of verbose statements which -// are not properly cleaned up. -#![allow(clippy::absurd_extreme_comparisons)] -#![allow(clippy::identity_op)] -#![allow(clippy::eq_op)] -#![allow(clippy::match_single_binding)] - -use crate::spread_layout_derive; - -#[test] -fn unit_struct_works() { - synstructure::test_derive! { - spread_layout_derive { - struct UnitStruct; - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for UnitStruct { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = [0u64, 0u64][(0u64 < 0u64) as ::core::primitive::usize]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = (false || false ); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - UnitStruct - } - - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - UnitStruct => {} - } - } - - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - UnitStruct => {} - } - } - } - }; - } - } -} - -#[test] -fn struct_works() { - synstructure::test_derive! { - spread_layout_derive { - struct NamedFields { - a: i32, - b: [u8; 32], - d: Box, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for NamedFields { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = [ - (((0u64 + ::FOOTPRINT) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT) - + as ::ink_storage::traits::SpreadLayout>::FOOTPRINT), - 0u64 - ][((((0u64 - + ::FOOTPRINT) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT) - + as ::ink_storage::traits::SpreadLayout>::FOOTPRINT) - < 0u64) as ::core::primitive::usize - ]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = ( - false || ( - ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || <[u8; 32] as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP - ) - || as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP - ) - ); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - NamedFields { - a : ::pull_spread(__key_ptr), - b : <[u8; 32] as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr), - d : as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr), - } - } - - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - NamedFields { - a: __binding_0, - b: __binding_1, - d: __binding_2, - } => { - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_2, - __key_ptr - ); - } - } - } - } - - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - NamedFields { - a: __binding_0, - b: __binding_1, - d: __binding_2, - } => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_2, - __key_ptr - ); - } - } - } - } - } - }; - } - } -} - -#[test] -fn one_variant_enum_works() { - synstructure::test_derive! { - spread_layout_derive { - enum OneVariantEnum { - A, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for OneVariantEnum { - #[allow(unused_comparisons)] - const FOOTPRINT : ::core::primitive::u64 = 1 + [0u64, 0u64][(0u64 < 0u64) as ::core::primitive::usize]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = (false || false); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - match <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) - { - 0u8 => OneVariantEnum::A, - _ => unreachable!("encountered invalid enum discriminant"), - } - } - - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - OneVariantEnum::A => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread( - &0u8, - __key_ptr - ); - } - } - } - } - - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - OneVariantEnum::A => {} - } - } - } - }; - } - } -} - -#[test] -fn enum_works() { - synstructure::test_derive! { - spread_layout_derive { - enum MixedEnum { - A, - B(i32, [u8; 32]), - C { a: i32, b: (bool, i32) }, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for MixedEnum { - #[allow(unused_comparisons)] - const FOOTPRINT : ::core::primitive::u64 = 1 + [ - 0u64 , - [ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT) - , - [ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ), - 0u64 - ] - [ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - < 0u64 - ) as ::core::primitive::usize - ] - ][ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - <[ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ), - 0u64 - ][ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - < 0u64 - ) as ::core::primitive::usize - ] - ) as ::core::primitive::usize - ] - ][ - ( - 0u64 <[ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ), - [ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ), - 0u64 - ][ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - < 0u64 - ) as ::core::primitive::usize - ] - ][ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <[u8; 32] as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - <[ - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ), - 0u64 - ][ - ( - ( - ( - 0u64 - + ::FOOTPRINT - ) - + <(bool, i32) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT - ) - < 0u64 - ) as ::core::primitive::usize - ] - ) as ::core::primitive::usize - ] - ) as ::core::primitive::usize - ]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = ( - ( - (false || false) - || ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || <[u8; 32] as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP - ) - ) - || ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || <(bool, i32) as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP - ) - ); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - match <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) - { - 0u8 => MixedEnum::A, - 1u8 => MixedEnum::B( - ::pull_spread(__key_ptr), - <[u8; 32] as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr), - ), - 2u8 => MixedEnum::C { - a: < i32 as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr ), - b: <(bool, i32) as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr), - }, - _ => unreachable!("encountered invalid enum discriminant"), - } - } - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - MixedEnum::A => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread( - &0u8, - __key_ptr - ); - } - } - MixedEnum::B(__binding_0, __binding_1,) => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread( - &1u8, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - } - MixedEnum::C { - a: __binding_0, - b: __binding_1, - } => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread( - &2u8, __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - MixedEnum::A => {} - MixedEnum::B(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - } - MixedEnum::C { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - } - }; - } - } -} - -#[test] -fn generic_struct_works() { - synstructure::test_derive! { - spread_layout_derive { - struct GenericStruct { - a: T1, - b: (T1, T2), - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for GenericStruct - where - T1: ::ink_storage::traits::SpreadLayout, - T2: ::ink_storage::traits::SpreadLayout - { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = [ - ((0u64 + ::FOOTPRINT) - + <(T1, T2) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT), - 0u64 - ][(((0u64 + ::FOOTPRINT) - + <(T1, T2) as ::ink_storage::traits::SpreadLayout>::FOOTPRINT) - < 0u64) as ::core::primitive::usize - ]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = ( - false || ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || < (T1, T2) as ::ink_storage::traits::SpreadLayout>::REQUIRES_DEEP_CLEAN_UP - ) - ); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - GenericStruct { - a: ::pull_spread( - __key_ptr - ), - b: <(T1, T2) as ::ink_storage::traits::SpreadLayout>::pull_spread( - __key_ptr - ), - } - } - - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - GenericStruct { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - GenericStruct { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - } - }; - } - } -} - -#[test] -fn generic_enum_works() { - synstructure::test_derive! { - spread_layout_derive { - enum GenericEnum { - Tuple(T1, T2), - Named { a: T1, b: T2 }, - } - } - expands to { - const _: () = { - impl ::ink_storage::traits::SpreadLayout for GenericEnum - where - T1: ::ink_storage::traits::SpreadLayout, - T2: ::ink_storage::traits::SpreadLayout - { - #[allow(unused_comparisons)] - const FOOTPRINT: ::core::primitive::u64 = 1 + [ - ((0u64 + ::FOOTPRINT) - + ::FOOTPRINT), - [ - ((0u64 + ::FOOTPRINT) - + ::FOOTPRINT), - 0u64 - ][(((0u64 - + ::FOOTPRINT) - + ::FOOTPRINT) - < 0u64) as ::core::primitive::usize] - ][(((0u64 + ::FOOTPRINT) - + ::FOOTPRINT) - < [ - ((0u64 + ::FOOTPRINT) - + ::FOOTPRINT), - 0u64 - ][(((0u64 - + ::FOOTPRINT) - + ::FOOTPRINT) - < 0u64) as ::core::primitive::usize]) as ::core::primitive::usize - ]; - - const REQUIRES_DEEP_CLEAN_UP : ::core::primitive::bool = ( - ( - false || ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || ::REQUIRES_DEEP_CLEAN_UP - ) - ) - || ( - ( - false - || ::REQUIRES_DEEP_CLEAN_UP - ) - || ::REQUIRES_DEEP_CLEAN_UP - ) - ); - - fn pull_spread(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> Self { - match <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::pull_spread(__key_ptr) - { - 0u8 => GenericEnum::Tuple( - ::pull_spread(__key_ptr), - ::pull_spread(__key_ptr), - ), - 1u8 => GenericEnum::Named { - a: ::pull_spread(__key_ptr), - b: ::pull_spread(__key_ptr), - }, - _ => unreachable!("encountered invalid enum discriminant"), - } - } - - fn push_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - GenericEnum::Tuple(__binding_0, __binding_1,) => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread(&0u8, __key_ptr); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - } - GenericEnum::Named { - a: __binding_0, - b: __binding_1, - } => { - { - <::core::primitive::u8 as ::ink_storage::traits::SpreadLayout>::push_spread(&1u8, __key_ptr); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::push_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - - fn clear_spread(&self, __key_ptr: &mut ::ink_storage::traits::KeyPtr) { - match self { - GenericEnum::Tuple(__binding_0, __binding_1,) => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - } - GenericEnum::Named { - a: __binding_0, - b: __binding_1, - } => { - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_0, - __key_ptr - ); - } - { - ::ink_storage::traits::SpreadLayout::clear_spread( - __binding_1, - __key_ptr - ); - } - } - } - } - } - }; - } - } -} diff --git a/crates/storage/derive/src/tests/storable.rs b/crates/storage/derive/src/tests/storable.rs new file mode 100644 index 00000000000..ef812de5c0d --- /dev/null +++ b/crates/storage/derive/src/tests/storable.rs @@ -0,0 +1,401 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// These tests are partly testing if code is expanded correctly. +// Hence the syntax contains a number of verbose statements which +// are not properly cleaned up. +#![allow(clippy::absurd_extreme_comparisons)] +#![allow(clippy::identity_op)] +#![allow(clippy::eq_op)] +#![allow(clippy::match_single_binding)] + +use crate::storable_derive; + +#[test] +fn unit_struct_works() { + crate::test_derive! { + storable_derive { + struct UnitStruct; + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for UnitStruct { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok(UnitStruct) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + UnitStruct => { } + } + } + } + }; + } + } +} + +#[test] +fn struct_works() { + crate::test_derive! { + storable_derive { + struct NamedFields { + a: i32, + b: [u8; 32], + d: Box, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for NamedFields { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + NamedFields { + a : ::decode(__input)?, + b : <[u8; 32] as ::ink_storage::traits::Storable>::decode(__input)?, + d : as ::ink_storage::traits::Storable>::decode(__input)?, + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + + NamedFields { + a: __binding_0, + b: __binding_1, + d: __binding_2, + } => { + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_2, + __dest + ); + } + } + } + } + } + }; + } + } +} + +#[test] +fn one_variant_enum_works() { + crate::test_derive! { + storable_derive { + enum OneVariantEnum { + A, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for OneVariantEnum { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + match <::core::primitive::u8 as ::ink_storage::traits::Storable>::decode(__input)? + { + 0u8 => OneVariantEnum::A, + _ => unreachable!("encountered invalid enum discriminant"), + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + OneVariantEnum::A => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + &0u8, + __dest + ); + } + } + } + } + } + }; + } + } +} + +#[test] +fn enum_works() { + crate::test_derive! { + storable_derive { + enum MixedEnum { + A, + B(i32, [u8; 32]), + C { a: i32, b: (bool, i32) }, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for MixedEnum { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + match <::core::primitive::u8 as ::ink_storage::traits::Storable>::decode(__input)? + { + 0u8 => MixedEnum::A, + 1u8 => MixedEnum::B( + ::decode(__input)?, + <[u8; 32] as ::ink_storage::traits::Storable>::decode(__input)?, + ), + 2u8 => MixedEnum::C { + a: < i32 as ::ink_storage::traits::Storable>::decode(__input)?, + b: <(bool, i32) as ::ink_storage::traits::Storable>::decode(__input)?, + }, + _ => unreachable!("encountered invalid enum discriminant"), + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + MixedEnum::A => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + &0u8, + __dest + ); + } + } + MixedEnum::B(__binding_0, __binding_1,) => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + &1u8, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + } + MixedEnum::C { + a: __binding_0, + b: __binding_1, + } => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + &2u8, __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + } + } + } + } + }; + } + } +} + +#[test] +fn generic_struct_works() { + crate::test_derive! { + storable_derive { + struct GenericStruct + where + T1: ::ink_storage::traits::Packed, + T2: ::ink_storage::traits::Packed, + { + a: T1, + b: (T1, T2), + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for GenericStruct + where + T1: ::ink_storage::traits::Packed, + T2: ::ink_storage::traits::Packed, + T1: ::ink_storage::traits::Storable, + (T1 , T2): ::ink_storage::traits::Storable + { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + GenericStruct { + a: ::decode( + __input + )?, + b: <(T1, T2) as ::ink_storage::traits::Storable>::decode( + __input + )?, + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + GenericStruct { + a: __binding_0, + b: __binding_1, + } => { + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + } + } + } + } + }; + } + } +} + +#[test] +fn generic_enum_works() { + crate::test_derive! { + storable_derive { + enum GenericEnum { + Tuple(T1, T2), + Named { a: T1, b: T2 }, + } + } + expands to { + const _: () = { + impl ::ink_storage::traits::Storable for GenericEnum + where + T1: ::ink_storage::traits::Storable, + T2: ::ink_storage::traits::Storable + { + #[inline(always)] + #[allow(non_camel_case_types)] + fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { + ::core::result::Result::Ok( + match <::core::primitive::u8 as ::ink_storage::traits::Storable>::decode(__input)? + { + 0u8 => GenericEnum::Tuple( + ::decode(__input)?, + ::decode(__input)?, + ), + 1u8 => GenericEnum::Named { + a: ::decode(__input)?, + b: ::decode(__input)?, + }, + _ => unreachable!("encountered invalid enum discriminant"), + } + ) + } + + #[inline(always)] + #[allow(non_camel_case_types)] + fn encode<__ink_O: ::scale::Output + ?::core::marker::Sized>(&self, __dest: &mut __ink_O) { + match self { + GenericEnum::Tuple(__binding_0, __binding_1,) => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&0u8, __dest); + } + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + } + GenericEnum::Named { + a: __binding_0, + b: __binding_1, + } => { + { + <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&1u8, __dest); + } + { + ::ink_storage::traits::Storable::encode( + __binding_0, + __dest + ); + } + { + ::ink_storage::traits::Storable::encode( + __binding_1, + __dest + ); + } + } + } + } + } + }; + } + } +} diff --git a/crates/storage/derive/src/tests/storage_layout.rs b/crates/storage/derive/src/tests/storage_layout.rs index f85b59e169a..59cba43543d 100644 --- a/crates/storage/derive/src/tests/storage_layout.rs +++ b/crates/storage/derive/src/tests/storage_layout.rs @@ -16,16 +16,16 @@ use crate::storage_layout_derive; #[test] fn unit_struct_works() { - synstructure::test_derive! { + crate::test_derive! { storage_layout_derive { struct UnitStruct; } expands to { const _: () = { impl ::ink_storage::traits::StorageLayout for UnitStruct { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new([]) + ::ink_metadata::layout::StructLayout::new(::core::stringify!(UnitStruct), []) ) } } @@ -36,29 +36,32 @@ fn unit_struct_works() { #[test] fn tuple_struct_works() { - synstructure::test_derive! { + crate::test_derive! { storage_layout_derive { struct TupleStruct(bool, u32, i64); } expands to { const _: () = { impl ::ink_storage::traits::StorageLayout for TupleStruct { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new([ - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ]) + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(TupleStruct), + [ + ::ink_metadata::layout::FieldLayout::new( + "0", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "1", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "2", + ::layout(__key), + ), + ] + ) ) } } @@ -69,7 +72,7 @@ fn tuple_struct_works() { #[test] fn named_fields_struct_works() { - synstructure::test_derive! { + crate::test_derive! { storage_layout_derive { struct NamedFieldsStruct { a: bool, @@ -80,22 +83,25 @@ fn named_fields_struct_works() { expands to { const _: () = { impl ::ink_storage::traits::StorageLayout for NamedFieldsStruct { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Struct( - ::ink_metadata::layout::StructLayout::new([ - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("a"), - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("b"), - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("c"), - ::layout(__key_ptr), - ), - ]) + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(NamedFieldsStruct), + [ + ::ink_metadata::layout::FieldLayout::new( + "a", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "b", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "c", + ::layout(__key), + ), + ] + ) ) } } @@ -106,41 +112,41 @@ fn named_fields_struct_works() { #[test] fn clike_enum_works() { - synstructure::test_derive! { + crate::test_derive! { storage_layout_derive { enum ClikeEnum { A, B, C } } expands to { const _: () = { impl ::ink_storage::traits::StorageLayout for ClikeEnum { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { - let dispatch_key = __key_ptr.advance_by(1); + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( - ::ink_metadata::layout::LayoutKey::from(dispatch_key), + ::core::stringify!(ClikeEnum), + ::ink_metadata::layout::LayoutKey::from(__key), [ { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(0usize), - ::ink_metadata::layout::StructLayout::new([]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(A), [] + ), ) }, { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(1usize), - ::ink_metadata::layout::StructLayout::new([]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(B), [] + ), ) }, { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(2usize), - ::ink_metadata::layout::StructLayout::new([]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(C), [] + ), ) }, ] @@ -155,7 +161,7 @@ fn clike_enum_works() { #[test] fn mixed_enum_works() { - synstructure::test_derive! { + crate::test_derive! { storage_layout_derive { enum MixedEnum { A, @@ -170,60 +176,62 @@ fn mixed_enum_works() { expands to { const _: () = { impl ::ink_storage::traits::StorageLayout for MixedEnum { - fn layout(__key_ptr: &mut ::ink_storage::traits::KeyPtr) -> ::ink_metadata::layout::Layout { - let dispatch_key = __key_ptr.advance_by(1); + fn layout(__key: &::ink_primitives::Key) -> ::ink_metadata::layout::Layout { ::ink_metadata::layout::Layout::Enum( ::ink_metadata::layout::EnumLayout::new( - ::ink_metadata::layout::LayoutKey::from(dispatch_key), + ::core::stringify!(MixedEnum), + ::ink_metadata::layout::LayoutKey::from(__key), [ { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(0usize), - ::ink_metadata::layout::StructLayout::new([]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(A), [] + ), ) }, { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(1usize), - ::ink_metadata::layout::StructLayout::new([ - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::None, - ::layout(__key_ptr), - ), - ]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(B), + [ + ::ink_metadata::layout::FieldLayout::new( + "0", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "1", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "2", + ::layout(__key), + ), + ] + ), ) }, { - let mut __variant_key_ptr = *__key_ptr; - let mut __key_ptr = &mut __variant_key_ptr; ( ::ink_metadata::layout::Discriminant::from(2usize), - ::ink_metadata::layout::StructLayout::new([ - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("a"), - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("b"), - ::layout(__key_ptr), - ), - ::ink_metadata::layout::FieldLayout::new( - ::core::option::Option::Some("c"), - ::layout(__key_ptr), - ), - ]), + ::ink_metadata::layout::StructLayout::new( + ::core::stringify!(C), + [ + ::ink_metadata::layout::FieldLayout::new( + "a", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "b", + ::layout(__key), + ), + ::ink_metadata::layout::FieldLayout::new( + "c", + ::layout(__key), + ), + ] + ), ) }, ] diff --git a/crates/storage/src/lazy/mapping.rs b/crates/storage/src/lazy/mapping.rs index c1d5b78ba6b..077c2eb8328 100644 --- a/crates/storage/src/lazy/mapping.rs +++ b/crates/storage/src/lazy/mapping.rs @@ -20,32 +20,29 @@ //! Instead it is just a simple wrapper around the contract storage facilities. use crate::traits::{ - pull_packed_root_opt, - push_packed_root, - ExtKeyPtr, - KeyPtr, - PackedLayout, - SpreadAllocate, - SpreadLayout, + AutoKey, + Item, + KeyHolder, + Packed, + Storable, }; use core::marker::PhantomData; - -use ink_env::hash::{ - Blake2x256, - HashOutput, -}; use ink_primitives::Key; +use scale::{ + Encode, + Error, + Input, + Output, +}; /// A mapping of key-value pairs directly into contract storage. /// /// # Important /// -/// If you use this data structure you must use the function -/// [`ink_lang::utils::initialize_contract`](https://paritytech.github.io/ink/ink_lang/utils/fn.initialize_contract.html) -/// in your contract's constructors! -/// -/// Note that in order to use this function your contract's storage struct must implement the -/// [`SpreadAllocate`](crate::traits::SpreadAllocate) trait. +/// The mapping requires its own pre-defined storage key where to store values. By default, +/// it is [`AutoKey`](crate::traits::AutoKey) and during compilation is calculated based on +/// the name of the structure and the field. But anyone can specify its storage key +/// via [`ManualKey`](crate::traits::ManualKey). /// /// This is an example of how you can do this: /// ```rust @@ -58,18 +55,21 @@ use ink_primitives::Key; /// /// # #[ink::contract] /// # mod my_module { -/// use ink_storage::{traits::SpreadAllocate, Mapping}; +/// use ink_storage::{traits::ManualKey, Mapping}; /// /// #[ink(storage)] -/// #[derive(SpreadAllocate)] +/// #[derive(Default)] /// pub struct MyContract { /// balances: Mapping, +/// allowance: Mapping>, /// } /// /// impl MyContract { /// #[ink(constructor)] /// pub fn new() -> Self { -/// ink_lang::utils::initialize_contract(Self::new_init) +/// let mut instance = Self::default(); +/// instance.new_init(); +/// instance /// } /// /// /// Default initializes the contract. @@ -78,6 +78,7 @@ use ink_primitives::Key; /// let value: Balance = Default::default(); /// self.balances.insert(&caller, &value); /// } +/// /// # #[ink(message)] /// # pub fn my_message(&self) { } /// } @@ -86,52 +87,63 @@ use ink_primitives::Key; /// /// More usage examples can be found [in the ink! examples](https://github.com/paritytech/ink/tree/master/examples). #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub struct Mapping { - offset_key: Key, - _marker: PhantomData (K, V)>, +pub struct Mapping { + #[allow(clippy::type_complexity)] + _marker: PhantomData (K, V, KeyType)>, } /// We implement this manually because the derived implementation adds trait bounds. -impl Default for Mapping { +impl Default for Mapping +where + V: Packed, + KeyType: KeyHolder, +{ fn default() -> Self { Self { - offset_key: Default::default(), _marker: Default::default(), } } } -impl core::fmt::Debug for Mapping { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("Mapping") - .field("offset_key", &self.offset_key) - .finish() - } -} - -impl Mapping { +impl Mapping +where + V: Packed, + KeyType: KeyHolder, +{ /// Creates a new empty `Mapping`. - fn new(offset_key: Key) -> Self { + pub fn new() -> Self { Self { - offset_key, _marker: Default::default(), } } } -impl Mapping +impl ::core::fmt::Debug for Mapping +where + V: Packed, + KeyType: KeyHolder, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Mapping") + .field("key", &KeyType::KEY) + .finish() + } +} + +impl Mapping where - K: PackedLayout, - V: PackedLayout, + K: Encode, + V: Packed, + KeyType: KeyHolder, { /// Insert the given `value` to the contract storage. #[inline] pub fn insert(&mut self, key: Q, value: &R) where Q: scale::EncodeLike, - R: scale::EncodeLike + PackedLayout, + R: scale::EncodeLike, { - push_packed_root(value, &self.storage_key(&key)); + ink_env::set_contract_storage(&(&KeyType::KEY, key), value); } /// Insert the given `value` to the contract storage. @@ -141,9 +153,9 @@ where pub fn insert_return_size(&mut self, key: Q, value: &R) -> Option where Q: scale::EncodeLike, - R: scale::EncodeLike + PackedLayout, + R: scale::EncodeLike, { - push_packed_root(value, &self.storage_key(&key)) + ink_env::set_contract_storage(&(&KeyType::KEY, key), value) } /// Get the `value` at `key` from the contract storage. @@ -154,7 +166,8 @@ where where Q: scale::EncodeLike, { - pull_packed_root_opt(&self.storage_key(&key)) + ink_env::get_contract_storage::<(&Key, Q), V>(&(&KeyType::KEY, key)) + .unwrap_or_else(|error| panic!("failed to get value in mapping: {:?}", error)) } /// Get the size of a value stored at `key` in the contract storage. @@ -165,7 +178,7 @@ where where Q: scale::EncodeLike, { - ink_env::contract_storage_contains(&self.storage_key(&key)) + ink_env::contains_contract_storage(&(&KeyType::KEY, key)) } /// Checks if a value is stored at the given `key` in the contract storage. @@ -176,93 +189,71 @@ where where Q: scale::EncodeLike, { - ink_env::contract_storage_contains(&self.storage_key(&key)).is_some() + ink_env::contains_contract_storage(&(&KeyType::KEY, key)).is_some() } /// Clears the value at `key` from storage. + #[inline] pub fn remove(&self, key: Q) where Q: scale::EncodeLike, { - let storage_key = self.storage_key(&key); - if ::REQUIRES_DEEP_CLEAN_UP { - // There are types which need to perform some action before being cleared. Here we - // indicate to those types that they should start tidying up. - if let Some(value) = self.get(key) { - ::clear_packed(&value, &storage_key); - } - } - ink_env::clear_contract_storage(&storage_key); - } - - /// Returns a `Key` pointer used internally by the storage API. - /// - /// This key is a combination of the `Mapping`'s internal `offset_key` - /// and the user provided `key`. - fn storage_key(&self, key: &Q) -> Key - where - Q: scale::EncodeLike, - { - let encodedable_key = (&self.offset_key, key); - let mut output = ::Type::default(); - ink_env::hash_encoded::(&encodedable_key, &mut output); - output.into() + ink_env::clear_contract_storage(&(&KeyType::KEY, key)); } } -impl SpreadLayout for Mapping { - const FOOTPRINT: u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: bool = false; - +impl Storable for Mapping +where + V: Packed, + KeyType: KeyHolder, +{ #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - // Note: There is no need to pull anything from the storage for the - // mapping type since it initializes itself entirely by the - // given key pointer. - Self::new(*ExtKeyPtr::next_for::(ptr)) - } + fn encode(&self, _dest: &mut T) {} #[inline] - fn push_spread(&self, ptr: &mut KeyPtr) { - // Note: The mapping type does not store any state in its associated - // storage region, therefore only the pointer has to be incremented. - ptr.advance_by(Self::FOOTPRINT); + fn decode(_input: &mut I) -> Result { + Ok(Default::default()) } +} - #[inline] - fn clear_spread(&self, ptr: &mut KeyPtr) { - // Note: The mapping type is not aware of its elements, therefore - // it is not possible to clean up after itself. - ptr.advance_by(Self::FOOTPRINT); - } +impl Item for Mapping +where + V: Packed, + Salt: KeyHolder, + InnerSalt: KeyHolder, +{ + type Type = Mapping; + type PreferredKey = InnerSalt; } -impl SpreadAllocate for Mapping { - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - // Note: The mapping type initializes itself entirely by the key pointer. - Self::new(*ExtKeyPtr::next_for::(ptr)) - } +impl KeyHolder for Mapping +where + V: Packed, + KeyType: KeyHolder, +{ + const KEY: Key = KeyType::KEY; } #[cfg(feature = "std")] const _: () = { use crate::traits::StorageLayout; use ink_metadata::layout::{ - CellLayout, Layout, LayoutKey, + RootLayout, }; - impl StorageLayout for Mapping + impl StorageLayout for Mapping where K: scale_info::TypeInfo + 'static, - V: scale_info::TypeInfo + 'static, + V: Packed + StorageLayout + scale_info::TypeInfo + 'static, + KeyType: KeyHolder + scale_info::TypeInfo + 'static, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Cell(CellLayout::new::(LayoutKey::from( - key_ptr.advance_by(1), - ))) + fn layout(_: &Key) -> Layout { + Layout::Root(RootLayout::new( + LayoutKey::from(&KeyType::KEY), + ::layout(&KeyType::KEY), + )) } } }; @@ -274,7 +265,7 @@ mod tests { #[test] fn insert_and_get_work() { ink_env::test::run_test::(|_| { - let mut mapping: Mapping = Mapping::new([0u8; 32].into()); + let mut mapping: Mapping = Mapping::new(); mapping.insert(&1, &2); assert_eq!(mapping.get(&1), Some(2)); @@ -286,7 +277,7 @@ mod tests { #[test] fn gets_default_if_no_key_set() { ink_env::test::run_test::(|_| { - let mapping: Mapping = Mapping::new([0u8; 32].into()); + let mapping: Mapping = Mapping::new(); assert_eq!(mapping.get(&1), None); Ok(()) @@ -297,26 +288,17 @@ mod tests { #[test] fn can_clear_entries() { ink_env::test::run_test::(|_| { - // We use `Pack` here since it `REQUIRES_DEEP_CLEAN_UP` - use crate::pack::Pack; - // Given - let mut mapping: Mapping = Mapping::new([0u8; 32].into()); - let mut deep_mapping: Mapping> = Mapping::new([1u8; 32].into()); + let mut mapping: Mapping = Mapping::new(); mapping.insert(&1, &2); assert_eq!(mapping.get(&1), Some(2)); - deep_mapping.insert(&1u8, &Pack::new(Pack::new(2u8))); - assert_eq!(deep_mapping.get(&1), Some(Pack::new(2u8))); - // When mapping.remove(&1); - deep_mapping.remove(&1); // Then assert_eq!(mapping.get(&1), None); - assert_eq!(deep_mapping.get(&1), None); Ok(()) }) @@ -326,20 +308,14 @@ mod tests { #[test] fn can_clear_unexistent_entries() { ink_env::test::run_test::(|_| { - // We use `Pack` here since it `REQUIRES_DEEP_CLEAN_UP` - use crate::pack::Pack; - // Given - let mapping: Mapping = Mapping::new([0u8; 32].into()); - let deep_mapping: Mapping> = Mapping::new([1u8; 32].into()); + let mapping: Mapping = Mapping::new(); // When mapping.remove(&1); - deep_mapping.remove(&1); // Then assert_eq!(mapping.get(&1), None); - assert_eq!(deep_mapping.get(&1), None); Ok(()) }) diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index d3b84b5f87e..8c746a3211b 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -15,7 +15,252 @@ //! Low-level collections and data structures to manage storage entities in the //! persisted contract storage. //! +//! The low-level collections are mainly used as building blocks for internals +//! of other higher-level storage collections. +//! //! These low-level collections are not aware of the elements they manage thus //! extra care has to be taken when operating directly on them. -pub mod mapping; +mod mapping; + +#[doc(inline)] +pub use self::mapping::Mapping; + +use crate::traits::{ + pull_storage, + push_storage, + AutoKey, + DecodeWrapper, + Item, + KeyHolder, + Storable, +}; +use core::marker::PhantomData; +use ink_primitives::Key; +use scale::{ + Error, + Input, + Output, +}; + +/// A simple wrapper around type to store it in a separate storage cell under own storage key. +/// If you want to update the value, first you need to `get` it, update, and after `set`. +/// +/// # Important +/// +/// The wrapper requires its own pre-defined storage key where to store value. By default, +/// it is [`AutoKey`](crate::traits::AutoKey) and during compilation is calculated based on +/// the name of the structure and the field. But anyone can specify its storage key +/// via [`ManualKey`](crate::traits::ManualKey). +/// +/// This is an example of how you can do this: +/// ```rust +/// # use ink_lang as ink; +/// # use ink_env::{ +/// # Environment, +/// # DefaultEnvironment, +/// # }; +/// # type AccountId = ::AccountId; +/// +/// # #[ink::contract] +/// # mod my_module { +/// use ink_storage::{traits::ManualKey, Lazy}; +/// +/// #[ink(storage)] +/// #[derive(Default)] +/// pub struct MyContract { +/// owner: Lazy, +/// balance: Lazy>, +/// } +/// +/// impl MyContract { +/// #[ink(constructor)] +/// pub fn new() -> Self { +/// let mut instance = Self::default(); +/// instance.new_init(); +/// instance +/// } +/// +/// /// Default initializes the contract. +/// fn new_init(&mut self) { +/// let caller = Self::env().caller(); +/// self.owner.set(&caller); +/// self.balance.set(&123456); +/// } +/// +/// # #[ink(message)] +/// # pub fn my_message(&self) { } +/// } +/// # } +/// ``` +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct Lazy { + _marker: PhantomData (V, KeyType)>, +} + +/// We implement this manually because the derived implementation adds trait bounds. +impl Default for Lazy +where + KeyType: KeyHolder, +{ + fn default() -> Self { + Self { + _marker: Default::default(), + } + } +} + +impl Lazy +where + KeyType: KeyHolder, +{ + /// Creates a new empty `Lazy`. + pub fn new() -> Self { + Self { + _marker: Default::default(), + } + } +} + +impl core::fmt::Debug for Lazy +where + KeyType: KeyHolder, +{ + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Lazy").field("key", &KeyType::KEY).finish() + } +} + +impl Lazy +where + V: Storable, + KeyType: KeyHolder, +{ + /// Get the `value` from the contract storage. + /// + /// Panics if no `value` exists. + pub fn get(&self) -> V { + pull_storage(&KeyType::KEY) + } +} + +impl Lazy +where + V: Storable + Default, + KeyType: KeyHolder, +{ + /// Get the `value` from the contract storage. + /// + /// Returns `Default::default()` if no `value` exists. + pub fn get_or_default(&self) -> V { + match ink_env::get_contract_storage::>(&KeyType::KEY) { + Ok(Some(wrapper)) => wrapper.0, + _ => Default::default(), + } + } +} + +impl Lazy +where + V: Storable, + KeyType: KeyHolder, +{ + /// Sets the given `value` to the contract storage. + pub fn set(&mut self, value: &V) { + push_storage(value, &KeyType::KEY); + } +} + +impl Storable for Lazy +where + KeyType: KeyHolder, +{ + #[inline(always)] + fn encode(&self, _dest: &mut T) {} + + #[inline(always)] + fn decode(_input: &mut I) -> Result { + Ok(Default::default()) + } +} + +impl Item for Lazy +where + Salt: KeyHolder, + InnerSalt: KeyHolder, + V: Item, +{ + type Type = Lazy; + type PreferredKey = InnerSalt; +} + +impl KeyHolder for Lazy +where + KeyType: KeyHolder, +{ + const KEY: Key = KeyType::KEY; +} + +#[cfg(feature = "std")] +const _: () = { + use crate::traits::StorageLayout; + use ink_metadata::layout::{ + Layout, + LayoutKey, + RootLayout, + }; + + impl StorageLayout for Lazy + where + V: StorageLayout + scale_info::TypeInfo + 'static, + KeyType: KeyHolder + scale_info::TypeInfo + 'static, + { + fn layout(_: &Key) -> Layout { + Layout::Root(RootLayout::new( + LayoutKey::from(&KeyType::KEY), + ::layout(&KeyType::KEY), + )) + } + } +}; + +#[cfg(test)] +mod tests { + use super::*; + use crate::traits::ManualKey; + + #[test] + fn set_and_get_work() { + ink_env::test::run_test::(|_| { + let mut storage: Lazy> = Lazy::new(); + storage.set(&2); + assert_eq!(storage.get(), 2); + + Ok(()) + }) + .unwrap() + } + + #[test] + fn gets_or_default_if_no_key_set() { + ink_env::test::run_test::(|_| { + let storage: Lazy> = Lazy::new(); + assert_eq!(storage.get_or_default(), 0); + + Ok(()) + }) + .unwrap() + } + + #[test] + #[should_panic(expected = "storage entry was empty")] + fn gets_failes_if_no_key_set() { + ink_env::test::run_test::(|_| { + let storage: Lazy> = Lazy::new(); + storage.get(); + + Ok(()) + }) + .unwrap() + } +} diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 9734b6028e7..358f6a1a63a 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -43,17 +43,19 @@ unused_extern_crates )] -#[cfg(all(test, feature = "std", feature = "ink-fuzz-tests"))] -#[macro_use(quickcheck)] -extern crate quickcheck_macros; - pub mod traits; -mod lazy; -mod pack; +#[allow(dead_code)] +pub(crate) mod lazy; #[cfg(test)] mod test_utils; #[doc(inline)] -pub use self::lazy::mapping::Mapping; +pub use self::lazy::{ + Lazy, + Mapping, +}; + +#[doc(inline)] +pub use self::traits::pull_or_init; diff --git a/crates/storage/src/pack.rs b/crates/storage/src/pack.rs deleted file mode 100644 index 68c6fe2448a..00000000000 --- a/crates/storage/src/pack.rs +++ /dev/null @@ -1,555 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::traits::{ - clear_spread_root, - forward_clear_packed, - forward_pull_packed, - forward_push_packed, - KeyPtr, - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use ink_prelude::vec::Vec; -use ink_primitives::Key; - -/// Packs the inner `T` so that it only occupies a single contract storage cell. -/// -/// # Note -/// -/// This is an important modular building stone in order to manage contract -/// storage occupation. By default, types try to distribute themselves onto -/// their respective contract storage area. However, upon packing them into -/// `Pack` they will be compressed to only ever make use of a single -/// contract storage cell. Sometimes this can be advantageous for performance -/// reasons. -/// -/// # Usage -/// -/// - A `Pack` instance is equivalent to `i32` in its storage occupation. -/// - A `Pack<(i32, i32)>` instance will occupy a single cell compared to -/// `(i32, i32)` which occupies a cell per `i32`. -/// - A `Lazy>` lazily loads a `Pack<[u8; 8]>` which occupies -/// a single cell whereas a `[u8; 8]` array would occupy 8 cells in total, -/// one for each `u8`. -/// - Rust collections will never use more than a single cell. So -/// `Pack>` and `LinkedList` will occupy the same amount of -/// cells, namely 1. -/// - Packs can be packed. So for example a -/// `Pack<(Pack<(i32, i32)>, Pack<[u8; 8]>)` uses just one cell instead of -/// two cells which is the case for `(Pack<(i32, i32)>, Pack<[u8; 8]>)`. -/// - Not all `storage` types can be packed. Only those that are implementing -/// the `PackedLayout` trait. For example `storage::Vec` does not implement -/// this trait and thus cannot be packed. -/// -/// As a general advice pack values together that are frequently used together. -/// Also pack many very small elements (e.g. `u8`, `bool`, `u16`) together. -#[derive(Debug, Clone)] -pub struct Pack -where - T: PackedLayout, -{ - /// The packed `T` value. - inner: T, - /// The key to load the packed value from. - /// - /// # Note - /// - /// This can be `None` on contract initialization, but will be - /// initialized with a concrete value on `pull_spread`. - key: Option, -} - -impl scale::Encode for Pack -where - T: scale::Encode + PackedLayout, -{ - #[inline] - fn size_hint(&self) -> usize { - ::size_hint(&self.inner) - } - - #[inline] - fn encode_to(&self, dest: &mut O) { - ::encode_to(&self.inner, dest) - } - - #[inline] - fn encode(&self) -> Vec { - ::encode(&self.inner) - } - - #[inline] - fn using_encoded R>(&self, f: F) -> R { - ::using_encoded(&self.inner, f) - } - - #[inline] - fn encoded_size(&self) -> usize { - ::encoded_size(&self.inner) - } -} - -impl scale::EncodeLike for Pack where T: scale::Encode + PackedLayout {} - -impl scale::Decode for Pack -where - T: scale::Decode + PackedLayout, -{ - fn decode(input: &mut I) -> Result { - ::decode(input).map(Self::new) - } -} - -impl Pack -where - T: PackedLayout, -{ - /// Creates a new packed value. - pub fn new(value: T) -> Self { - Self { - inner: value, - key: None, - } - } - - /// Returns a shared reference to the packed value. - pub fn as_inner(pack: &Pack) -> &T { - &pack.inner - } - - /// Returns an exclusive reference to the packed value. - pub fn as_inner_mut(pack: &mut Pack) -> &mut T { - &mut pack.inner - } -} - -impl Drop for Pack -where - T: PackedLayout, -{ - fn drop(&mut self) { - if let Some(key) = self.key { - clear_spread_root::(&self.inner, &key) - } - } -} - -#[cfg(feature = "std")] -const _: () = { - use crate::traits::StorageLayout; - use ink_metadata::layout::{ - CellLayout, - Layout, - LayoutKey, - }; - use scale_info::TypeInfo; - - impl StorageLayout for Pack - where - T: PackedLayout + TypeInfo + 'static, - { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Cell(CellLayout::new::(LayoutKey::from(key_ptr.advance_by(1)))) - } - } -}; - -impl SpreadLayout for Pack -where - T: PackedLayout, -{ - const FOOTPRINT: u64 = 1; - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - let inner = forward_pull_packed::(ptr); - Self { - inner, - key: Some(*ptr.key()), - } - } - - fn push_spread(&self, ptr: &mut KeyPtr) { - forward_push_packed::(Self::as_inner(self), ptr) - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - forward_clear_packed::(Self::as_inner(self), ptr) - } -} - -impl SpreadAllocate for Pack -where - T: PackedLayout + Default, -{ - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - Self { - inner: T::default(), - key: Some(*ptr.key()), - } - } -} - -impl PackedLayout for Pack -where - T: PackedLayout, -{ - fn pull_packed(&mut self, at: &Key) { - ::pull_packed(Self::as_inner_mut(self), at) - } - fn push_packed(&self, at: &Key) { - ::push_packed(Self::as_inner(self), at) - } - fn clear_packed(&self, at: &Key) { - ::clear_packed(Self::as_inner(self), at) - } -} - -impl PackedAllocate for Pack -where - T: PackedAllocate + Default, -{ - fn allocate_packed(&mut self, at: &Key) { - ::allocate_packed(Self::as_inner_mut(self), at) - } -} - -impl From for Pack -where - T: PackedLayout, -{ - fn from(value: T) -> Self { - Self::new(value) - } -} - -impl Default for Pack -where - T: Default + PackedLayout, -{ - fn default() -> Self { - Self::new(Default::default()) - } -} - -impl core::ops::Deref for Pack -where - T: PackedLayout, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - Self::as_inner(self) - } -} - -impl core::ops::DerefMut for Pack -where - T: PackedLayout, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - Self::as_inner_mut(self) - } -} - -impl core::cmp::PartialEq for Pack -where - T: PartialEq + PackedLayout, -{ - fn eq(&self, other: &Self) -> bool { - PartialEq::eq(Self::as_inner(self), Self::as_inner(other)) - } -} - -impl core::cmp::Eq for Pack where T: Eq + PackedLayout {} - -impl core::cmp::PartialOrd for Pack -where - T: PartialOrd + PackedLayout, -{ - fn partial_cmp(&self, other: &Self) -> Option { - PartialOrd::partial_cmp(Self::as_inner(self), Self::as_inner(other)) - } - fn lt(&self, other: &Self) -> bool { - PartialOrd::lt(Self::as_inner(self), Self::as_inner(other)) - } - fn le(&self, other: &Self) -> bool { - PartialOrd::le(Self::as_inner(self), Self::as_inner(other)) - } - fn ge(&self, other: &Self) -> bool { - PartialOrd::ge(Self::as_inner(self), Self::as_inner(other)) - } - fn gt(&self, other: &Self) -> bool { - PartialOrd::gt(Self::as_inner(self), Self::as_inner(other)) - } -} - -impl core::cmp::Ord for Pack -where - T: core::cmp::Ord + PackedLayout, -{ - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - Ord::cmp(Self::as_inner(self), Self::as_inner(other)) - } -} - -impl core::fmt::Display for Pack -where - T: core::fmt::Display + PackedLayout, -{ - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - core::fmt::Display::fmt(Self::as_inner(self), f) - } -} - -impl core::hash::Hash for Pack -where - T: core::hash::Hash + PackedLayout, -{ - fn hash(&self, state: &mut H) { - Self::as_inner(self).hash(state); - } -} - -impl core::convert::AsRef for Pack -where - T: PackedLayout, -{ - fn as_ref(&self) -> &T { - Self::as_inner(self) - } -} - -impl core::convert::AsMut for Pack -where - T: PackedLayout, -{ - fn as_mut(&mut self) -> &mut T { - Self::as_inner_mut(self) - } -} - -impl ink_prelude::borrow::Borrow for Pack -where - T: PackedLayout, -{ - fn borrow(&self) -> &T { - Self::as_inner(self) - } -} - -impl ink_prelude::borrow::BorrowMut for Pack -where - T: PackedLayout, -{ - fn borrow_mut(&mut self) -> &mut T { - Self::as_inner_mut(self) - } -} - -#[cfg(test)] -mod tests { - use super::Pack; - use crate::traits::{ - pull_packed_root, - push_packed_root, - KeyPtr, - PackedLayout, - SpreadLayout, - }; - use core::{ - cmp::Ordering, - convert::{ - AsMut, - AsRef, - }, - ops::{ - Deref, - DerefMut, - }, - }; - use ink_env::test::DefaultAccounts; - use ink_prelude::borrow::{ - Borrow, - BorrowMut, - }; - use ink_primitives::Key; - - type ComplexTuple = (u8, [i32; 4], (bool, i32)); - - fn complex_value() -> ComplexTuple { - (b'A', [0x00; 4], (true, 42)) - } - - #[test] - fn new_works() { - let mut expected = complex_value(); - let mut pack = Pack::new(expected); - assert_eq!( as Deref>::deref(&pack), &expected); - assert_eq!( as DerefMut>::deref_mut(&mut pack), &mut expected); - assert_eq!( as AsRef<_>>::as_ref(&pack), &expected); - assert_eq!( as AsMut<_>>::as_mut(&mut pack), &mut expected); - assert_eq!(Borrow::::borrow(&pack), &expected); - assert_eq!( - BorrowMut::::borrow_mut(&mut pack), - &mut expected - ); - assert_eq!(Pack::as_inner(&pack), &expected); - assert_eq!(Pack::as_inner_mut(&mut pack), &mut expected); - assert_eq!(pack.inner, expected); - } - - #[test] - fn from_works() { - let mut expected = complex_value(); - let mut from = Pack::from(expected); - assert_eq!(from, Pack::new(expected)); - assert_eq!(Pack::as_inner(&from), &expected); - assert_eq!(Pack::as_inner_mut(&mut from), &mut expected); - assert_eq!(from.inner, expected); - } - - #[test] - fn default_works() { - use core::fmt::Debug; - fn assert_default() - where - T: Debug + Default + PartialEq + PackedLayout, - { - let pack_default = as Default>::default(); - assert_eq!(pack_default.inner, ::default()); - } - assert_default::(); - assert_default::(); - assert_default::>(); - assert_default::>(); - } - - #[test] - fn partial_eq_works() { - let b1 = Pack::new(b'X'); - let b2 = Pack::new(b'Y'); - let b3 = Pack::new(b'X'); - assert!( as PartialEq>::ne(&b1, &b2)); - assert!( as PartialEq>::eq(&b1, &b3)); - } - - #[test] - fn partial_ord_works() { - let b1 = Pack::new(1); - let b2 = Pack::new(2); - let b3 = Pack::new(1); - assert_eq!( - as PartialOrd>::partial_cmp(&b1, &b2), - Some(Ordering::Less) - ); - assert_eq!( - as PartialOrd>::partial_cmp(&b2, &b1), - Some(Ordering::Greater) - ); - assert_eq!( - as PartialOrd>::partial_cmp(&b1, &b3), - Some(Ordering::Equal) - ); - // Less-than - assert!( as PartialOrd>::lt(&b1, &b2)); - // Less-than-or-equals - assert!( as PartialOrd>::le(&b1, &b2)); - assert!( as PartialOrd>::le(&b1, &b3)); - // Greater-than - assert!( as PartialOrd>::gt(&b2, &b1)); - // Greater-than-or-equals - assert!( as PartialOrd>::ge(&b2, &b1)); - assert!( as PartialOrd>::ge(&b3, &b1)); - } - - fn run_test(f: F) - where - F: FnOnce(DefaultAccounts), - { - ink_env::test::run_test::(|default_accounts| { - f(default_accounts); - Ok(()) - }) - .unwrap() - } - - #[test] - fn spread_layout_push_pull_works() { - run_test(|_| { - let p1 = Pack::new((b'A', [0x00; 4], (true, 42))); - assert_eq!(*p1, (b'A', [0x00; 4], (true, 42))); - let root_key = Key::from([0x42; 32]); - SpreadLayout::push_spread(&p1, &mut KeyPtr::from(root_key)); - // Now load another instance of a pack from the same key and check - // if both instances are equal: - let p2 = SpreadLayout::pull_spread(&mut KeyPtr::from(root_key)); - assert_eq!(p1, p2); - }) - } - - #[test] - #[should_panic(expected = "storage entry was empty")] - fn spread_layout_clear_works() { - run_test(|_| { - let p1 = Pack::new((b'A', [0x00; 4], (true, 42))); - assert_eq!(*p1, (b'A', [0x00; 4], (true, 42))); - let root_key = Key::from([0x42; 32]); - SpreadLayout::push_spread(&p1, &mut KeyPtr::from(root_key)); - // Now load another instance of a pack from the same key and check - // if both instances are equal: - let p2 = SpreadLayout::pull_spread(&mut KeyPtr::from(root_key)); - assert_eq!(p1, p2); - // Clearing the underlying storage of p2 immediately so that - // loading another instance of pack again should panic. - SpreadLayout::clear_spread(&p2, &mut KeyPtr::from(root_key)); - let p3 = SpreadLayout::pull_spread(&mut KeyPtr::from(root_key)); - assert_eq!(p1, p3); - }) - } - - #[test] - fn spread_and_packed_layout_are_equal() { - run_test(|_| { - // Push as spread, pull as packed: - let p1 = Pack::new((b'A', [0x00; 4], (true, 42))); - assert_eq!(*p1, (b'A', [0x00; 4], (true, 42))); - let root_key = Key::from([0x42; 32]); - SpreadLayout::push_spread(&p1, &mut KeyPtr::from(root_key)); - let p2 = pull_packed_root::>(&root_key); - assert_eq!(p1, p2); - // Push as packed, pull as spread: - let root_key2 = Key::from([0x43; 32]); - push_packed_root(&p2, &root_key2); - let p3 = SpreadLayout::pull_spread(&mut KeyPtr::from(root_key2)); - assert_eq!(p2, p3); - }) - } -} - -#[cfg(all(test, feature = "std", feature = "ink-fuzz-tests"))] -use quickcheck::{ - Arbitrary, - Gen, -}; - -#[cfg(all(test, feature = "std", feature = "ink-fuzz-tests"))] -impl Arbitrary for Pack { - fn arbitrary(g: &mut Gen) -> Pack { - let a = ::arbitrary(g); - Pack::new(a) - } -} diff --git a/crates/storage/src/test_utils.rs b/crates/storage/src/test_utils.rs index 87afc2e6d23..f1b5df61132 100644 --- a/crates/storage/src/test_utils.rs +++ b/crates/storage/src/test_utils.rs @@ -28,49 +28,19 @@ where .unwrap() } -/// Creates two tests: -/// (1) Tests if an object which is `push_spread`-ed to storage results in exactly -/// the same object when it is `pull_spread`-ed again. Subsequently the object -/// undergoes the same test for `push_packed` and `pull_packed`. -/// (2) Tests if `clear_spread` removes the object properly from storage. +/// Creates test to verify that the type for primitives is atomic and the same. #[macro_export] -macro_rules! push_pull_works_for_primitive { - ( $name:ty, [$($value:expr),*] ) => { +macro_rules! item_works_for_primitive { + ( $ty:ty ) => { paste::item! { #[test] #[allow(non_snake_case)] - fn [<$name _pull_push_works>] () { + fn [<$ty _item_works>] () { $crate::test_utils::run_test(|| { - $({ - let x: $name = $value; - let key = ink_primitives::Key::from([0x42; 32]); - let key2 = ink_primitives::Key::from([0x77; 32]); - $crate::traits::push_spread_root(&x, &key); - let y: $name = $crate::traits::pull_spread_root(&key); - assert_eq!(x, y); - $crate::traits::push_packed_root(&x, &key2); - let z: $name = $crate::traits::pull_packed_root(&key2); - assert_eq!(x, z); - })* - }) - } - - #[test] - #[should_panic(expected = "storage entry was empty")] - #[allow(non_snake_case)] - fn [<$name _clean_works>]() { - $crate::test_utils::run_test(|| { - $({ - let x: $name = $value; - let key = ink_primitives::Key::from([0x42; 32]); - $crate::traits::push_spread_root(&x, &key); - // Works since we just populated the storage. - let y: $name = $crate::traits::pull_spread_root(&key); - assert_eq!(x, y); - $crate::traits::clear_spread_root(&x, &key); - // Panics since it loads eagerly from cleared storage. - let _: $name = $crate::traits::pull_spread_root(&key); - })* + assert_eq!( + ::core::any::TypeId::of::<$ty>(), + ::core::any::TypeId::of::<<$ty as $crate::traits::Item<$crate::traits::ManualKey<123>>>::Type>() + ); }) } } diff --git a/crates/storage/src/traits/impls/arrays.rs b/crates/storage/src/traits/impls/arrays.rs index a89aa75baad..c6998f2ebdb 100644 --- a/crates/storage/src/traits/impls/arrays.rs +++ b/crates/storage/src/traits/impls/arrays.rs @@ -12,113 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::traits::{ - KeyPtr, - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use array_init::array_init; -use ink_primitives::Key; - -impl SpreadLayout for [T; N] -where - T: SpreadLayout, -{ - const FOOTPRINT: u64 = N as u64 * ::FOOTPRINT; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - fn push_spread(&self, ptr: &mut KeyPtr) { - for elem in self { - ::push_spread(elem, ptr) - } - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - for elem in self { - ::clear_spread(elem, ptr) - } - } - - #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - array_init::<_, T, N>(|_| ::pull_spread(ptr)) - } -} - -impl SpreadAllocate for [T; N] -where - T: SpreadAllocate, -{ - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - array_init::<_, T, N>(|_| ::allocate_spread(ptr)) - } -} - -impl PackedLayout for [T; N] -where - T: PackedLayout, -{ - #[inline] - fn push_packed(&self, at: &Key) { - for elem in self { - ::push_packed(elem, at) - } - } - - #[inline] - fn clear_packed(&self, at: &Key) { - for elem in self { - ::clear_packed(elem, at) - } - } - - #[inline] - fn pull_packed(&mut self, at: &Key) { - for elem in self { - ::pull_packed(elem, at) - } - } -} - -impl PackedAllocate for [T; N] -where - T: PackedAllocate, -{ - #[inline] - fn allocate_packed(&mut self, at: &Key) { - for elem in self { - ::allocate_packed(elem, at) - } - } -} - #[cfg(test)] mod tests { - use crate::push_pull_works_for_primitive; + use crate::item_works_for_primitive; type Array = [i32; 4]; - push_pull_works_for_primitive!( - Array, - [ - [0, 1, 2, 3], - [i32::MAX, i32::MIN, i32::MAX, i32::MIN], - [Default::default(), i32::MAX, Default::default(), i32::MIN] - ] - ); + item_works_for_primitive!(Array); type ArrayTuples = [(i32, i32); 2]; - push_pull_works_for_primitive!( - ArrayTuples, - [ - [(0, 1), (2, 3)], - [(i32::MAX, i32::MIN), (i32::MIN, i32::MAX)], - [ - (Default::default(), i32::MAX), - (Default::default(), i32::MIN) - ] - ] - ); + item_works_for_primitive!(ArrayTuples); } diff --git a/crates/storage/src/traits/impls/collections.rs b/crates/storage/src/traits/impls/collections.rs deleted file mode 100644 index 6d29bdc2316..00000000000 --- a/crates/storage/src/traits/impls/collections.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use crate::traits::{ - impls::{ - forward_allocate_packed, - forward_clear_packed, - forward_pull_packed, - forward_push_packed, - }, - KeyPtr, - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use ink_prelude::{ - collections::{ - BTreeMap as StdBTreeMap, - BTreeSet as StdBTreeSet, - BinaryHeap as StdBinaryHeap, - LinkedList as StdLinkedList, - VecDeque as StdVecDeque, - }, - vec::Vec, -}; -use ink_primitives::Key; - -impl SpreadLayout for StdBTreeMap -where - K: PackedLayout + Ord, - V: PackedLayout, -{ - const FOOTPRINT: u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - forward_pull_packed::(ptr) - } - - #[inline] - fn push_spread(&self, ptr: &mut KeyPtr) { - forward_push_packed::(self, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut KeyPtr) { - forward_clear_packed::(self, ptr) - } -} - -impl SpreadAllocate for StdBTreeMap -where - K: PackedAllocate + Ord, - V: PackedAllocate, -{ - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - forward_allocate_packed::(ptr) - } -} - -impl PackedLayout for StdBTreeMap -where - K: PackedLayout + Ord, - V: PackedLayout, -{ - fn push_packed(&self, at: &Key) { - for (key, val) in self { - ::push_packed(key, at); - ::push_packed(val, at); - } - } - - fn clear_packed(&self, at: &Key) { - for (key, val) in self { - ::clear_packed(key, at); - ::clear_packed(val, at); - } - } - - fn pull_packed(&mut self, at: &Key) { - // We cannot mutate keys in a map so we can forward pull signals - // only to the values of a map. - for val in self.values_mut() { - ::pull_packed(val, at); - } - } -} - -impl PackedAllocate for StdBTreeMap -where - K: PackedAllocate + Ord, - V: PackedAllocate, -{ - fn allocate_packed(&mut self, at: &Key) { - // We cannot mutate keys in a map so we can forward allocate signals - // only to the values of a map. - for val in self.values_mut() { - ::allocate_packed(val, at); - } - } -} - -impl SpreadLayout for StdBTreeSet -where - T: PackedLayout + Ord, -{ - const FOOTPRINT: u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - forward_pull_packed::(ptr) - } - - #[inline] - fn push_spread(&self, ptr: &mut KeyPtr) { - forward_push_packed::(self, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut KeyPtr) { - forward_clear_packed::(self, ptr) - } -} - -impl SpreadAllocate for StdBTreeSet -where - T: PackedAllocate + Ord, -{ - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - forward_allocate_packed::(ptr) - } -} - -impl PackedLayout for StdBTreeSet -where - T: PackedLayout + Ord, -{ - fn push_packed(&self, at: &Key) { - for key in self { - ::push_packed(key, at); - } - } - - fn clear_packed(&self, at: &Key) { - for key in self { - ::clear_packed(key, at); - } - } - - #[inline] - fn pull_packed(&mut self, _at: &Key) { - // We cannot mutate keys in a set so we cannot forward pull signals. - } -} - -impl PackedAllocate for StdBTreeSet -where - T: PackedAllocate + Ord, -{ - #[inline] - fn allocate_packed(&mut self, _at: &Key) { - // We cannot mutate keys in a set so we cannot forward pull signals. - } -} - -impl SpreadLayout for StdBinaryHeap -where - T: PackedLayout + Ord, -{ - const FOOTPRINT: u64 = 1; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - forward_pull_packed::(ptr) - } - - #[inline] - fn push_spread(&self, ptr: &mut KeyPtr) { - forward_push_packed::(self, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut KeyPtr) { - forward_clear_packed::(self, ptr) - } -} - -impl SpreadAllocate for StdBinaryHeap -where - T: PackedAllocate + Ord, -{ - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - forward_allocate_packed::(ptr) - } -} - -impl PackedLayout for StdBinaryHeap -where - T: PackedLayout + Ord, -{ - fn push_packed(&self, at: &Key) { - for value in self { - ::push_packed(value, at); - } - } - - fn clear_packed(&self, at: &Key) { - for value in self { - ::clear_packed(value, at); - } - } - - #[inline] - fn pull_packed(&mut self, _at: &Key) { - // We cannot mutate keys in a heap so we cannot forward pull signals. - } -} - -impl PackedAllocate for StdBinaryHeap -where - T: PackedAllocate + Ord, -{ - #[inline] - fn allocate_packed(&mut self, _at: &Key) { - // We cannot mutate keys in a heap so we cannot forward pull signals. - } -} - -macro_rules! impl_push_at_for_collection { - ( $($collection:ident),* $(,)? ) => { - $( - impl_always_packed_layout!($collection, deep: ::REQUIRES_DEEP_CLEAN_UP); - - impl PackedLayout for $collection - where - T: PackedLayout, - { - fn push_packed(&self, at: &Key) { - for elem in self { - ::push_packed(elem, at) - } - } - - fn clear_packed(&self, at: &Key) { - for elem in self { - ::clear_packed(elem, at) - } - } - - fn pull_packed(&mut self, at: &Key) { - for elem in self { - ::pull_packed(elem, at) - } - } - } - - impl PackedAllocate for $collection - where - T: PackedAllocate, - { - fn allocate_packed(&mut self, at: &Key) { - for elem in self { - ::allocate_packed(elem, at) - } - } - } - )* - }; -} -impl_push_at_for_collection!(Vec, StdLinkedList, StdVecDeque,); diff --git a/crates/storage/src/traits/impls/fuzz_tests.rs b/crates/storage/src/traits/impls/fuzz_tests.rs deleted file mode 100644 index 47326aa1e27..00000000000 --- a/crates/storage/src/traits/impls/fuzz_tests.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Fuzz tests for some storage primitives. - -#[cfg(all(test, feature = "std", feature = "ink-fuzz-tests"))] -use quickcheck::TestResult; - -#[cfg(all(test, feature = "std", feature = "ink-fuzz-tests"))] -use std::convert::AsMut; - -/// Receives a slice, returns an array. -fn clone_into_array(slice: &[T]) -> A -where - A: Default + AsMut<[T]>, - T: Clone, -{ - let mut a = A::default(); - >::as_mut(&mut a).clone_from_slice(slice); - a -} - -/// Tests if a fuzzed `[i32; 32]` array results in the same object when -/// pushed/pulled from storage (for `spread` and `packed`). -#[quickcheck] -fn fuzz_pull_push_pull_array(x: Vec) -> TestResult { - // We want to have only vectors of length 32 fuzzed in here. - // The reason is that quickcheck does not directly support - // Array's as a parameter to be fuzzed. So we use this - // workaround of asking for a Vec with length 32 and convert - // it to an array with 32 elements subsequently. - // - // The guided fuzzing will notice that every Vec of greater/smaller - // length is always discarded and aim to input vectors of length 32. - if x.len() != 32 { - return TestResult::discard() - } - - ink_env::test::run_test::(|_| { - let key = ink_primitives::Key::from([0x42; 32]); - let key2 = ink_primitives::Key::from([0x77; 32]); - - let arr: [i32; 32] = clone_into_array(&x[0..32]); - crate::traits::push_spread_root(&arr, &key); - - let y: [i32; 32] = crate::traits::pull_spread_root(&key); - assert_eq!(arr, y); - - crate::traits::push_packed_root(&arr, &key2); - let z: [i32; 32] = crate::traits::pull_packed_root(&key2); - assert_eq!(arr, z); - - Ok(()) - }) - .unwrap(); - TestResult::from_bool(true) -} - -/// Tests if a fuzzed `String` results in the same object when pushed/pulled -/// from storage (for `spread` and `packed`). -#[cfg(feature = "ink-fuzz-tests")] -#[quickcheck] -fn fuzz_pull_push_pull_string(x: String) { - ink_env::test::run_test::(|_| { - let key = ink_primitives::Key::from([0x42; 32]); - let key2 = ink_primitives::Key::from([0x77; 32]); - - crate::traits::push_spread_root(&x, &key); - let y: String = crate::traits::pull_spread_root(&key); - assert_eq!(x, y); - - crate::traits::push_packed_root(&x, &key2); - let z: String = crate::traits::pull_packed_root(&key2); - assert_eq!(x, z); - Ok(()) - }) - .unwrap() -} diff --git a/crates/storage/src/traits/impls/mod.rs b/crates/storage/src/traits/impls/mod.rs index 3467e816601..b36257c1424 100644 --- a/crates/storage/src/traits/impls/mod.rs +++ b/crates/storage/src/traits/impls/mod.rs @@ -12,172 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -macro_rules! impl_always_packed_layout { - ( $name:ident < $($frag:ident),+ >, deep: $deep:expr ) => { - impl<$($frag),+> $crate::traits::SpreadLayout for $name < $($frag),+ > - where - $( - $frag: $crate::traits::PackedLayout, - )+ - { - const FOOTPRINT: ::core::primitive::u64 = 1_u64; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = $deep; - - #[inline] - fn pull_spread(ptr: &mut $crate::traits::KeyPtr) -> Self { - $crate::traits::impls::forward_pull_packed::(ptr) - } - - #[inline] - fn push_spread(&self, ptr: &mut $crate::traits::KeyPtr) { - $crate::traits::impls::forward_push_packed::(self, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut $crate::traits::KeyPtr) { - $crate::traits::impls::forward_clear_packed::(self, ptr) - } - } - - impl<$($frag),+> $crate::traits::SpreadAllocate for $name < $($frag),+ > - where - Self: ::core::default::Default, - $( - $frag: $crate::traits::PackedAllocate, - )+ - { - #[inline] - fn allocate_spread(ptr: &mut $crate::traits::KeyPtr) -> Self { - $crate::traits::impls::forward_allocate_packed::(ptr) - } - } - }; - ( $name:ty, deep: $deep:expr ) => { - impl $crate::traits::SpreadLayout for $name - where - Self: $crate::traits::PackedLayout, - { - const FOOTPRINT: ::core::primitive::u64 = 1_u64; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = $deep; - - #[inline] - fn pull_spread(ptr: &mut $crate::traits::KeyPtr) -> Self { - $crate::traits::impls::forward_pull_packed::(ptr) - } - - #[inline] - fn push_spread(&self, ptr: &mut $crate::traits::KeyPtr) { - $crate::traits::impls::forward_push_packed::(self, ptr) - } - - #[inline] - fn clear_spread(&self, ptr: &mut $crate::traits::KeyPtr) { - $crate::traits::impls::forward_clear_packed::(self, ptr) - } - } - - impl $crate::traits::SpreadAllocate for $name - where - Self: $crate::traits::PackedLayout + ::core::default::Default, - { - #[inline] - fn allocate_spread(ptr: &mut $crate::traits::KeyPtr) -> Self { - $crate::traits::impls::forward_allocate_packed::(ptr) - } - } - }; -} - mod arrays; -mod collections; mod prims; mod tuples; -#[cfg(all(test, feature = "ink-fuzz-tests"))] -mod fuzz_tests; - -use super::{ - allocate_packed_root, - clear_packed_root, - pull_packed_root, - push_packed_root, - PackedAllocate, - PackedLayout, -}; -use crate::traits::{ - ExtKeyPtr as _, - KeyPtr, -}; - -/// Returns the greater of both values. -const fn max(a: u64, b: u64) -> u64 { - [a, b][(a > b) as usize] -} - -/// Pulls an instance of type `T` in packed fashion from the contract storage. -/// -/// Loads the instance from the storage location identified by `ptr`. -/// The storage entity is expected to be decodable in its packed form. -/// -/// # Note -/// -/// Use this utility function to use a packed pull operation for the type -/// instead of a spread storage layout pull operation. -#[inline] -pub fn forward_pull_packed(ptr: &mut KeyPtr) -> T -where - T: PackedLayout, -{ - pull_packed_root::(ptr.next_for::()) -} - -/// Allocates an instance of type `T` in packed fashion to the contract storage. -/// -/// This default initializes the entity at the storage location identified -/// by `ptr`. The storage entity is expected to be decodable in its packed form. -/// -/// # Note -/// -/// Use this utility function to use a packed allocate operation for the type -/// instead of a spread storage layout allocation operation. -#[inline] -pub fn forward_allocate_packed(ptr: &mut KeyPtr) -> T -where - T: PackedAllocate + Default, -{ - allocate_packed_root::(ptr.next_for::()) -} - -/// Pushes an instance of type `T` in packed fashion to the contract storage. -/// -/// Stores the instance to the storage location identified by `ptr`. -/// The storage entity is expected to be encodable in its packed form. -/// -/// # Note -/// -/// Use this utility function to use a packed push operation for the type -/// instead of a spread storage layout push operation. -#[inline] -pub fn forward_push_packed(entity: &T, ptr: &mut KeyPtr) -where - T: PackedLayout, -{ - push_packed_root::(entity, ptr.next_for::()); -} - -/// Clears an instance of type `T` in packed fashion from the contract storage. -/// -/// Clears the instance from the storage location identified by `ptr`. -/// The cleared storage entity is expected to be encoded in its packed form. -/// -/// # Note -/// -/// Use this utility function to use a packed clear operation for the type -/// instead of a spread storage layout clear operation. -#[inline] -pub fn forward_clear_packed(entity: &T, ptr: &mut KeyPtr) -where - T: PackedLayout, -{ - clear_packed_root::(entity, ptr.next_for::()) -} +pub(crate) mod storage; diff --git a/crates/storage/src/traits/impls/prims.rs b/crates/storage/src/traits/impls/prims.rs index d46ae140a69..d5a54be7f6d 100644 --- a/crates/storage/src/traits/impls/prims.rs +++ b/crates/storage/src/traits/impls/prims.rs @@ -12,335 +12,34 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::max; -use crate::traits::{ - KeyPtr, - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use ink_env::{ - AccountId, - Hash, -}; -use ink_prelude::{ - boxed::Box, - string::String, -}; -use ink_primitives::Key; - -macro_rules! impl_layout_for_primitive { - ( $($ty:ty),* $(,)? ) => { - $( - impl_always_packed_layout!($ty, deep: false); - impl $crate::traits::PackedLayout for $ty { - #[inline] - fn pull_packed(&mut self, _at: &::ink_primitives::Key) {} - #[inline] - fn push_packed(&self, _at: &::ink_primitives::Key) {} - #[inline] - fn clear_packed(&self, _at: &::ink_primitives::Key) {} - } - impl $crate::traits::PackedAllocate for $ty { - #[inline] - fn allocate_packed(&mut self, _at: &::ink_primitives::Key) {} - } - )* - }; -} -#[rustfmt::skip] -impl_layout_for_primitive!( - // We do not include `f32` and `f64` since Wasm contracts currently - // do not support them since they are non deterministic. We might add them - // to this list once we add deterministic support for those primitives. - Key, Hash, AccountId, (), - String, - bool, - u8, u16, u32, u64, u128, - i8, i16, i32, i64, i128, -); - -impl SpreadLayout for Option -where - T: SpreadLayout, -{ - const FOOTPRINT: u64 = 1 + ::FOOTPRINT; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - fn push_spread(&self, ptr: &mut KeyPtr) { - ::push_spread(&(self.is_some() as u8), ptr); - if let Some(value) = self { - ::push_spread(value, ptr); - } else { - ptr.advance_by(::FOOTPRINT); - } - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - // We do not really need the reference to 0 (zero) - // in order to clean-up the `bool` value from the storage. - // However the API is demanding a reference so we give it one. - ::clear_spread(&0, ptr); - if let Some(value) = self { - ::clear_spread(value, ptr) - } else { - ptr.advance_by(::FOOTPRINT); - } - } - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - match ::pull_spread(ptr) { - 0u8 => { - ptr.advance_by(::FOOTPRINT); - None - } - 1u8 => Some(::pull_spread(ptr)), - _ => unreachable!("invalid Option discriminant"), - } - } -} - -impl SpreadAllocate for Option -where - T: SpreadLayout, -{ - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - ptr.advance_by(::FOOTPRINT); - None - } -} - -impl PackedLayout for Option -where - T: PackedLayout, -{ - #[inline] - fn push_packed(&self, at: &Key) { - if let Some(value) = self { - ::push_packed(value, at) - } - } - - #[inline] - fn clear_packed(&self, at: &Key) { - if let Some(value) = self { - ::clear_packed(value, at) - } - } - - #[inline] - fn pull_packed(&mut self, at: &Key) { - if let Some(value) = self { - ::pull_packed(value, at) - } - } -} - -impl PackedAllocate for Option -where - T: PackedAllocate, -{ - #[inline] - fn allocate_packed(&mut self, at: &Key) { - // Note: Maybe this is not needed since `Option` is alwyas default - // initialized as `None` so this cannot really occur. - if let Some(value) = self { - ::allocate_packed(value, at) - } - } -} - -impl SpreadLayout for Result -where - T: SpreadLayout, - E: SpreadLayout, -{ - const FOOTPRINT: u64 = 1 + max( - ::FOOTPRINT, - ::FOOTPRINT, - ); - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP - || ::REQUIRES_DEEP_CLEAN_UP; - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - match ::pull_spread(ptr) { - 0 => Ok(::pull_spread(ptr)), - 1 => Err(::pull_spread(ptr)), - _ => unreachable!("invalid Result discriminant"), - } - } - - fn push_spread(&self, ptr: &mut KeyPtr) { - match self { - Ok(value) => { - ::push_spread(&0, ptr); - ::push_spread(value, ptr); - } - Err(error) => { - ::push_spread(&1, ptr); - ::push_spread(error, ptr); - } - } - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - // Clear the discriminant, same for all variants. - ::clear_spread(&0, ptr); - match self { - Ok(value) => { - ::clear_spread(value, ptr); - } - Err(error) => { - ::clear_spread(error, ptr); - } - } - } -} - -impl PackedLayout for Result -where - T: PackedLayout, - E: PackedLayout, -{ - #[inline] - fn push_packed(&self, at: &Key) { - match self { - Ok(value) => ::push_packed(value, at), - Err(error) => ::push_packed(error, at), - } - } - - #[inline] - fn clear_packed(&self, at: &Key) { - match self { - Ok(value) => ::clear_packed(value, at), - Err(error) => ::clear_packed(error, at), - } - } - - #[inline] - fn pull_packed(&mut self, at: &Key) { - match self { - Ok(value) => ::pull_packed(value, at), - Err(error) => ::pull_packed(error, at), - } - } -} - -impl SpreadLayout for Box -where - T: SpreadLayout, -{ - const FOOTPRINT: u64 = ::FOOTPRINT; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - Box::new(::pull_spread(ptr)) - } - - fn push_spread(&self, ptr: &mut KeyPtr) { - ::push_spread(self, ptr) - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - ::clear_spread(self, ptr) - } -} - -impl SpreadAllocate for Box -where - T: SpreadAllocate, -{ - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - Box::new(::allocate_spread(ptr)) - } -} - -impl PackedLayout for Box -where - T: PackedLayout, -{ - #[inline] - fn push_packed(&self, at: &Key) { - ::push_packed(self, at) - } - - #[inline] - fn clear_packed(&self, at: &Key) { - ::clear_packed(self, at) - } - - #[inline] - fn pull_packed(&mut self, at: &Key) { - ::pull_packed(&mut *self, at) - } -} - -impl PackedAllocate for Box -where - T: PackedAllocate, -{ - #[inline] - fn allocate_packed(&mut self, at: &Key) { - ::allocate_packed(&mut *self, at) - } -} - #[cfg(test)] mod tests { - use crate::push_pull_works_for_primitive; + use crate::item_works_for_primitive; use ink_env::AccountId; - use ink_primitives::Key; - push_pull_works_for_primitive!(bool, [false, true]); - push_pull_works_for_primitive!( - String, - [Default::default(), String::from("Hello, World!")] - ); - push_pull_works_for_primitive!( - Key, - [ - Key::from([0x00; 32]), - Key::from([0x42; 32]), - Key::from([0xFF; 32]) - ] - ); - push_pull_works_for_primitive!( - AccountId, - [ - AccountId::from([0x00; 32]), - AccountId::from([0x42; 32]), - AccountId::from([0xFF; 32]) - ] - ); - push_pull_works_for_primitive!(i8, [0, Default::default(), 1, i8::MIN, i8::MAX]); - push_pull_works_for_primitive!(i16, [0, Default::default(), 2, i16::MIN, i16::MAX]); - push_pull_works_for_primitive!(i32, [0, Default::default(), 3, i32::MIN, i32::MAX]); - push_pull_works_for_primitive!(i64, [0, Default::default(), 4, i64::MIN, i64::MAX]); - push_pull_works_for_primitive!( - i128, - [0, Default::default(), 5, i128::MIN, i128::MAX] - ); - push_pull_works_for_primitive!(u8, [0, Default::default(), 10, u8::MIN, u8::MAX]); - push_pull_works_for_primitive!(u16, [0, Default::default(), 20, u16::MIN, u16::MAX]); - push_pull_works_for_primitive!(u32, [0, Default::default(), 30, u32::MIN, u32::MAX]); - push_pull_works_for_primitive!(u64, [0, Default::default(), 40, u64::MIN, u64::MAX]); - push_pull_works_for_primitive!( - u128, - [0, Default::default(), 50, u128::MIN, u128::MAX] - ); + item_works_for_primitive!(bool); + item_works_for_primitive!(String); + item_works_for_primitive!(AccountId); + item_works_for_primitive!(i8); + item_works_for_primitive!(i16); + item_works_for_primitive!(i32); + item_works_for_primitive!(i64); + item_works_for_primitive!(i128); + item_works_for_primitive!(u8); + item_works_for_primitive!(u16); + item_works_for_primitive!(u32); + item_works_for_primitive!(u64); + item_works_for_primitive!(u128); type OptionU8 = Option; - push_pull_works_for_primitive!(OptionU8, [Some(13u8), None]); + item_works_for_primitive!(OptionU8); type ResultU8 = Result; - push_pull_works_for_primitive!(ResultU8, [Ok(13u8), Err(false)]); + item_works_for_primitive!(ResultU8); type BoxU8 = Box; - push_pull_works_for_primitive!(BoxU8, [Box::new(27u8)]); + item_works_for_primitive!(BoxU8); type BoxOptionU8 = Box>; - push_pull_works_for_primitive!(BoxOptionU8, [Box::new(Some(27)), Box::new(None)]); + item_works_for_primitive!(BoxOptionU8); } diff --git a/crates/storage/src/traits/impls/storage.rs b/crates/storage/src/traits/impls/storage.rs new file mode 100644 index 00000000000..39e222bc23d --- /dev/null +++ b/crates/storage/src/traits/impls/storage.rs @@ -0,0 +1,107 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::traits::{ + storage::Storable, + AutoItem, + Item, + KeyHolder, + Packed, +}; +use core::{ + fmt::Debug, + marker::PhantomData, +}; +use ink_primitives::{ + Key, + KeyComposer, +}; +use scale::{ + Error, + Input, + Output, +}; + +/// Auto key type means that the storage key should be calculated automatically. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct AutoKey; + +impl KeyHolder for AutoKey { + const KEY: Key = 0; +} + +/// Manual key type specifies the storage key. +#[derive(Default, Copy, Clone, Eq, PartialEq, PartialOrd, Debug)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct ManualKey(PhantomData Salt>); + +impl KeyHolder for ManualKey { + const KEY: Key = KeyComposer::concat(KEY, Salt::KEY); +} + +/// Resolver key type selects between preferred key and autogenerated key. +/// If the `L` type is `AutoKey` it returns auto-generated `R` else `L`. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Debug)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct ResolverKey(PhantomData (L, R)>); + +impl KeyHolder for ResolverKey { + /// `KEY` of the `AutoKey` is zero. If left key is zero, then use right manual key. + const KEY: Key = if L::KEY == 0 { R::KEY } else { L::KEY }; +} + +impl AutoItem> for T +where + T: Item, + T: Item>::PreferredKey, ManualKey>>, + Salt: KeyHolder, +{ + type Type = >::PreferredKey, ManualKey>, + >>::Type; +} + +impl

Packed for P where P: scale::Decode + scale::Encode {} + +impl

Storable for P +where + P: Packed, +{ + #[inline] + fn encode(&self, dest: &mut T) { + scale::Encode::encode_to(self, dest) + } + + #[inline] + fn decode(input: &mut I) -> Result { + scale::Decode::decode(input) + } +} + +impl

KeyHolder for P +where + P: Packed, +{ + const KEY: Key = 0; +} + +impl Item for P +where + P: Packed, + Salt: KeyHolder, +{ + type Type = P; + type PreferredKey = AutoKey; +} diff --git a/crates/storage/src/traits/impls/tuples.rs b/crates/storage/src/traits/impls/tuples.rs index b01f9e02313..04b65e968b5 100644 --- a/crates/storage/src/traits/impls/tuples.rs +++ b/crates/storage/src/traits/impls/tuples.rs @@ -12,166 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::traits::{ - KeyPtr, - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use ink_primitives::Key; - -macro_rules! impl_layout_for_tuple { - ( $($frag:ident),* $(,)? ) => { - impl<$($frag),*> SpreadLayout for ($($frag),* ,) - where - $( - $frag: SpreadLayout, - )* - { - const FOOTPRINT: ::core::primitive::u64 = - 0_u64 $(+ <$frag as SpreadLayout>::FOOTPRINT)*; - const REQUIRES_DEEP_CLEAN_UP: ::core::primitive::bool = - false $(|| <$frag as SpreadLayout>::REQUIRES_DEEP_CLEAN_UP)*; - - #[inline] - fn push_spread(&self, ptr: &mut KeyPtr) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as SpreadLayout>::push_spread($frag, ptr); - )* - } - - #[inline] - fn clear_spread(&self, ptr: &mut KeyPtr) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as SpreadLayout>::clear_spread($frag, ptr); - )* - } - - #[inline] - fn pull_spread(ptr: &mut KeyPtr) -> Self { - ( - $( - <$frag as SpreadLayout>::pull_spread(ptr), - )* - ) - } - } - - impl<$($frag),*> SpreadAllocate for ($($frag),* ,) - where - $( - $frag: SpreadAllocate, - )* - { - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - ( - $( - <$frag as SpreadAllocate>::allocate_spread(ptr), - )* - ) - } - } - - impl<$($frag),*> PackedLayout for ($($frag),* ,) - where - $( - $frag: PackedLayout, - )* - { - #[inline] - fn push_packed(&self, at: &Key) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as PackedLayout>::push_packed($frag, at); - )* - } - - #[inline] - fn clear_packed(&self, at: &Key) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as PackedLayout>::clear_packed($frag, at); - )* - } - - #[inline] - fn pull_packed(&mut self, at: &Key) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as PackedLayout>::pull_packed($frag, at); - )* - } - } - - impl<$($frag),*> PackedAllocate for ($($frag),* ,) - where - $( - $frag: PackedAllocate, - )* - { - #[inline] - fn allocate_packed(&mut self, at: &Key) { - #[allow(non_snake_case)] - let ($($frag),*,) = self; - $( - <$frag as PackedAllocate>::allocate_packed($frag, at); - )* - } - } - } -} -impl_layout_for_tuple!(A); -impl_layout_for_tuple!(A, B); -impl_layout_for_tuple!(A, B, C); -impl_layout_for_tuple!(A, B, C, D); -impl_layout_for_tuple!(A, B, C, D, E); -impl_layout_for_tuple!(A, B, C, D, E, F); -impl_layout_for_tuple!(A, B, C, D, E, F, G); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H, I); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H, I, J); - #[cfg(test)] mod tests { - use crate::push_pull_works_for_primitive; + use crate::item_works_for_primitive; type TupleSix = (i32, u32, String, u8, bool, Box>); - push_pull_works_for_primitive!( - TupleSix, - [ - ( - -1, - 1, - String::from("foobar"), - 13, - true, - Box::new(Some(i32::MIN)) - ), - ( - i32::MIN, - u32::MAX, - String::from("❤ ♡ ❤ ♡ ❤"), - Default::default(), - false, - Box::new(Some(i32::MAX)) - ), - ( - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default() - ) - ] - ); + item_works_for_primitive!(TupleSix); } diff --git a/crates/storage/src/traits/keyptr.rs b/crates/storage/src/traits/keyptr.rs deleted file mode 100644 index 356bdd12425..00000000000 --- a/crates/storage/src/traits/keyptr.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::SpreadLayout; -use ink_primitives::Key; -pub use ink_primitives::KeyPtr; - -/// Extension trait to make `KeyPtr` simpler to use for `T: SpreadLayout` types. -pub trait ExtKeyPtr { - /// Advances the key pointer by the same amount of the footprint of the - /// generic type parameter of `T` and returns the old value. - fn next_for(&mut self) -> &Key - where - T: SpreadLayout; -} - -impl ExtKeyPtr for KeyPtr { - fn next_for(&mut self) -> &Key - where - T: SpreadLayout, - { - self.advance_by(::FOOTPRINT) - } -} diff --git a/crates/storage/src/traits/layout/impls.rs b/crates/storage/src/traits/layout/impls.rs index 5457c9f932f..a671d8b3327 100644 --- a/crates/storage/src/traits/layout/impls.rs +++ b/crates/storage/src/traits/layout/impls.rs @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::StorageLayout; use crate::traits::{ - ExtKeyPtr as _, - KeyPtr, - SpreadLayout, + Packed, + StorageLayout, }; use ink_env::{ AccountId, @@ -34,7 +32,11 @@ use ink_metadata::layout::{ }; use ink_prelude::{ boxed::Box, - collections::BTreeMap, + collections::{ + BTreeMap, + BTreeSet, + VecDeque, + }, string::String, vec::Vec, }; @@ -45,8 +47,8 @@ macro_rules! impl_storage_layout_for_primitives { ( $($name:ty),* $(,)? ) => { $( impl StorageLayout for $name { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Cell(CellLayout::new::<$name>(LayoutKey::from(key_ptr.advance_by(1)))) + fn layout(key: &Key) -> Layout { + Layout::Leaf(CellLayout::new::<$name>(LayoutKey::from(key))) } } )* @@ -54,7 +56,7 @@ macro_rules! impl_storage_layout_for_primitives { } #[rustfmt::skip] impl_storage_layout_for_primitives!( - Key, Hash, AccountId, String, + Hash, AccountId, String, bool, char, (), u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, @@ -65,16 +67,15 @@ macro_rules! impl_storage_layout_for_arrays { $( impl StorageLayout for [T; $len] where - T: StorageLayout + SpreadLayout, + T: StorageLayout + Packed, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { + fn layout(key: &Key) -> Layout { let len: u32 = $len; - let elem_footprint = ::FOOTPRINT; + // Generic type is atomic, so it doesn't take any cell Layout::Array(ArrayLayout::new( - LayoutKey::from(key_ptr.next_for::<[T; $len]>()), + LayoutKey::from(key), len, - elem_footprint, - ::layout(&mut key_ptr.clone()), + ::layout(&key), )) } } @@ -90,42 +91,84 @@ impl_storage_layout_for_arrays!( ); macro_rules! impl_layout_for_tuple { - ( $($frag:ident),* $(,)? ) => { - impl<$($frag),*> StorageLayout for ($($frag),* ,) - where - $( - $frag: StorageLayout, - )* - { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Struct( - StructLayout::new([ - $( - FieldLayout::new(None, <$frag as StorageLayout>::layout(key_ptr)), - )* - ]) - ) + ( $(($frag:ident, $id:literal)),* $(,)? ) => { + const _: () = { + // The name of the tuple looks like `(A)`, `(A, B)` ... `(A, B, ..., J)` + const TUPLE_NAME: &'static str = stringify!(($($frag),*)); + + impl<$($frag),*> StorageLayout for ($($frag),* ,) + where + $( + $frag: StorageLayout, + )* + { + fn layout(key: &Key) -> Layout { + Layout::Struct( + StructLayout::new( + TUPLE_NAME, + [ + $( + FieldLayout::new( + ::core::stringify!($id), + <$frag as StorageLayout>::layout(key) + ), + )* + ] + ) + ) + } } - } + }; } } -impl_layout_for_tuple!(A); -impl_layout_for_tuple!(A, B); -impl_layout_for_tuple!(A, B, C); -impl_layout_for_tuple!(A, B, C, D); -impl_layout_for_tuple!(A, B, C, D, E); -impl_layout_for_tuple!(A, B, C, D, E, F); -impl_layout_for_tuple!(A, B, C, D, E, F, G); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H, I); -impl_layout_for_tuple!(A, B, C, D, E, F, G, H, I, J); + +impl_layout_for_tuple!((A, 0)); +impl_layout_for_tuple!((A, 0), (B, 1)); +impl_layout_for_tuple!((A, 0), (B, 1), (C, 2)); +impl_layout_for_tuple!((A, 0), (B, 1), (C, 2), (D, 3)); +impl_layout_for_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4)); +impl_layout_for_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5)); +impl_layout_for_tuple!((A, 0), (B, 1), (C, 2), (D, 3), (E, 4), (F, 5), (G, 6)); +impl_layout_for_tuple!( + (A, 0), + (B, 1), + (C, 2), + (D, 3), + (E, 4), + (F, 5), + (G, 6), + (H, 7) +); +impl_layout_for_tuple!( + (A, 0), + (B, 1), + (C, 2), + (D, 3), + (E, 4), + (F, 5), + (G, 6), + (H, 7), + (I, 8) +); +impl_layout_for_tuple!( + (A, 0), + (B, 1), + (C, 2), + (D, 3), + (E, 4), + (F, 5), + (G, 6), + (H, 7), + (I, 8), + (J, 9) +); impl StorageLayout for Box where T: StorageLayout, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - ::layout(key_ptr) + fn layout(key: &Key) -> Layout { + ::layout(key) } } @@ -133,19 +176,19 @@ impl StorageLayout for Option where T: StorageLayout, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - let dispatch_key = key_ptr.advance_by(1); + fn layout(key: &Key) -> Layout { Layout::Enum(EnumLayout::new( - *dispatch_key, + "Option", + key, [ + (Discriminant::from(0), StructLayout::new("None", Vec::new())), ( - Discriminant::from(0), - StructLayout::new([FieldLayout::new( - None, - ::layout(&mut key_ptr.clone()), - )]), + Discriminant::from(1), + StructLayout::new( + "Some", + [FieldLayout::new("0", ::layout(key))], + ), ), - (Discriminant::from(1), StructLayout::new(Vec::new())), ], )) } @@ -156,24 +199,24 @@ where T: StorageLayout, E: StorageLayout, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - let dispatch_key = key_ptr.advance_by(1); + fn layout(key: &Key) -> Layout { Layout::Enum(EnumLayout::new( - *dispatch_key, + "Result", + *key, [ ( Discriminant::from(0), - StructLayout::new([FieldLayout::new( - None, - ::layout(&mut key_ptr.clone()), - )]), + StructLayout::new( + "Ok", + [FieldLayout::new("0", ::layout(key))], + ), ), ( Discriminant::from(1), - StructLayout::new([FieldLayout::new( - None, - ::layout(&mut key_ptr.clone()), - )]), + StructLayout::new( + "Err", + [FieldLayout::new("1", ::layout(key))], + ), ), ], )) @@ -182,23 +225,37 @@ where impl StorageLayout for Vec where - T: TypeInfo + 'static, + T: TypeInfo + 'static + Packed, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Cell(CellLayout::new::(LayoutKey::from( - key_ptr.advance_by(1), - ))) + fn layout(key: &Key) -> Layout { + Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) } } impl StorageLayout for BTreeMap where - K: TypeInfo + 'static, - V: TypeInfo + 'static, + K: TypeInfo + 'static + Packed, + V: TypeInfo + 'static + Packed, +{ + fn layout(key: &Key) -> Layout { + Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + } +} + +impl StorageLayout for BTreeSet +where + T: TypeInfo + 'static + Packed, +{ + fn layout(key: &Key) -> Layout { + Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + } +} + +impl StorageLayout for VecDeque +where + T: TypeInfo + 'static + Packed, { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - Layout::Cell(CellLayout::new::(LayoutKey::from( - key_ptr.advance_by(1), - ))) + fn layout(key: &Key) -> Layout { + Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) } } diff --git a/crates/storage/src/traits/layout/mod.rs b/crates/storage/src/traits/layout/mod.rs index f285e41a133..1c4793ce148 100644 --- a/crates/storage/src/traits/layout/mod.rs +++ b/crates/storage/src/traits/layout/mod.rs @@ -14,10 +14,6 @@ mod impls; -#[cfg(test)] -mod tests; - -use crate::traits::KeyPtr; use ink_env::hash::{ Blake2x256, Keccak256, @@ -27,14 +23,15 @@ use ink_metadata::layout::{ CryptoHasher, Layout, }; +use ink_primitives::Key; /// Implemented by types that have a storage layout. pub trait StorageLayout { /// Returns the static storage layout of `Self`. /// - /// The given key pointer is guiding the allocation of static fields onto + /// The given storage key is guiding the allocation of static fields onto /// the contract storage regions. - fn layout(key_ptr: &mut KeyPtr) -> Layout; + fn layout(key: &Key) -> Layout; } /// Types implementing this trait are supported layouting crypto hashers. diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index f89c1f580be..d1c985e2d64 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -15,210 +15,91 @@ //! Traits and interfaces to operate with storage entities. //! //! Generally a type is said to be a storage entity if it implements the -//! `SpreadLayout` trait. This defines certain constants and routines in order +//! `Item` trait. This defines certain constants and routines in order //! to tell a smart contract how to load and store instances of this type //! from and to the contract's storage. //! -//! The `PackedLayout` trait can then be implemented on top of the `SpreadLayout` -//! for types that further allow to be stored in the contract storage in a more -//! compressed format to a single storage cell. +//! The `Packed` shows that the type is packed and can be stored +//! into single storage cell. Some collections works only with packed structures. mod impls; -mod keyptr; -mod optspec; -mod packed; -mod spread; +mod storage; #[cfg(feature = "std")] mod layout; +#[macro_use] +#[doc(hidden)] +pub mod pull_or_init; + #[cfg(feature = "std")] pub use self::layout::{ LayoutCryptoHasher, StorageLayout, }; -pub(crate) use self::optspec::pull_packed_root_opt; pub use self::{ - impls::{ - forward_allocate_packed, - forward_clear_packed, - forward_pull_packed, - forward_push_packed, - }, - keyptr::{ - ExtKeyPtr, - KeyPtr, - }, - packed::{ - PackedAllocate, - PackedLayout, + impls::storage::{ + AutoKey, + ManualKey, + ResolverKey, }, - spread::{ - SpreadAllocate, - SpreadLayout, - FOOTPRINT_CLEANUP_THRESHOLD, + storage::{ + AutoItem, + Item, + KeyHolder, + OnCallInitializer, + Packed, + Storable, }, }; use ink_primitives::Key; pub use ink_storage_derive::{ - PackedLayout, - SpreadAllocate, - SpreadLayout, + Item, + KeyHolder, + Storable, StorageLayout, }; +use scale::{ + Decode, + Encode, +}; -/// Pulls an instance of type `T` from the contract storage using spread layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being pulled from. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using spread layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`SpreadLayout`]. -pub fn pull_spread_root(root_key: &Key) -> T -where - T: SpreadLayout, -{ - let mut ptr = KeyPtr::from(*root_key); - ::pull_spread(&mut ptr) -} - -/// Pulls an instance of type `T` from the contract storage using spread layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being pulled from. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using spread layout. -/// - Users should prefer using this function directly instead of using the -/// trait method on [`SpreadAllocate`]. -pub fn allocate_spread_root(root_key: &Key) -> T -where - T: SpreadAllocate, -{ - let mut ptr = KeyPtr::from(*root_key); - ::allocate_spread(&mut ptr) -} - -/// Clears the entity from the contract storage using spread layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being cleared from. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using spread layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`SpreadLayout`]. -pub fn clear_spread_root(entity: &T, root_key: &Key) -where - T: SpreadLayout, -{ - let mut ptr = KeyPtr::from(*root_key); - ::clear_spread(entity, &mut ptr); -} +#[repr(transparent)] +pub(crate) struct DecodeWrapper(pub S); -/// Pushes the entity to the contract storage using spread layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being pushed to. -/// -/// # Note -/// -/// - The routine will push the given entity to the contract storage using -/// spread layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`SpreadLayout`]. -pub fn push_spread_root(entity: &T, root_key: &Key) -where - T: SpreadLayout, -{ - let mut ptr = KeyPtr::from(*root_key); - ::push_spread(entity, &mut ptr); +impl Decode for DecodeWrapper { + #[inline(always)] + fn decode(input: &mut I) -> Result { + Ok(Self(S::decode(input)?)) + } } -/// Pulls an instance of type `T` from the contract storage using packed layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being pulled from. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using packed layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`PackedLayout`]. -pub fn pull_packed_root(root_key: &Key) -> T +/// Pulls an instance of type `T` from the contract storage using decode and its storage key. +pub fn pull_storage(key: &Key) -> T where - T: PackedLayout, + T: Storable, { - let mut entity = ink_env::get_contract_storage::(root_key) - .expect("could not properly decode storage entry") - .expect("storage entry was empty"); - ::pull_packed(&mut entity, root_key); - entity + match ink_env::get_contract_storage::>(key) { + Ok(Some(wrapper)) => wrapper.0, + Ok(None) => panic!("storage entry was empty"), + Err(_) => panic!("could not properly decode storage entry"), + } } -/// Allocates an instance of type `T` to the contract storage using packed layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being allocated to. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using packed layout. -/// - Users should prefer using this function directly instead of using the -/// trait method on [`PackedAllocate`]. -pub fn allocate_packed_root(root_key: &Key) -> T -where - T: PackedAllocate + Default, -{ - let mut entity = ::default(); - ::allocate_packed(&mut entity, root_key); - entity -} +#[repr(transparent)] +pub(crate) struct EncodeWrapper<'a, S: Storable>(pub &'a S); -/// Pushes the entity to the contract storage using packed layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being pushed to. -/// -/// # Note -/// -/// - The routine will push the given entity to the contract storage using -/// packed layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`PackedLayout`]. -pub fn push_packed_root(entity: &T, root_key: &Key) -> Option -where - T: PackedLayout, -{ - ::push_packed(entity, root_key); - ink_env::set_contract_storage(root_key, entity) +impl<'a, S: Storable> Encode for EncodeWrapper<'a, S> { + #[inline(always)] + fn encode_to(&self, dest: &mut T) { + self.0.encode(dest) + } } -/// Clears the entity from the contract storage using packed layout. -/// -/// The root key denotes the offset into the contract storage where the -/// instance of type `T` is being cleared from. -/// -/// # Note -/// -/// - The routine assumes that the instance has previously been stored to -/// the contract storage using packed layout. -/// - Users should prefer using this function directly instead of using the -/// trait methods on [`PackedLayout`]. -pub fn clear_packed_root(entity: &T, root_key: &Key) +/// Pushes the entity to the contract storage using encode and storage key. +pub fn push_storage(entity: &T, key: &Key) -> Option where - T: PackedLayout, + T: Storable, { - ::clear_packed(entity, root_key); - ink_env::clear_contract_storage(root_key); + ink_env::set_contract_storage::>(key, &EncodeWrapper(entity)) } diff --git a/crates/storage/src/traits/optspec.rs b/crates/storage/src/traits/optspec.rs deleted file mode 100644 index 1c0965c39d0..00000000000 --- a/crates/storage/src/traits/optspec.rs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Implement specialized routines for managing Option storage entities. -//! -//! These are mere optimizations compared to the non-specialized root functions. -//! The specializations make use of the storage entry state (occupied or vacant) -//! in order to store the option's state thus using less storage in total. - -use super::PackedLayout; -use ink_primitives::Key; - -pub fn pull_packed_root_opt(root_key: &Key) -> Option -where - T: PackedLayout, -{ - ink_env::get_contract_storage::(root_key) - .unwrap_or_else(|error| { - panic!( - "failed to pull packed from root key {}: {:?}", - root_key, error - ) - }) - .map(|mut value| { - // In case the contract storage is occupied at the root key - // we handle the Option as if it was a T. - ::pull_packed(&mut value, root_key); - value - }) -} diff --git a/crates/storage/src/traits/packed.rs b/crates/storage/src/traits/packed.rs deleted file mode 100644 index 97c654765b6..00000000000 --- a/crates/storage/src/traits/packed.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::{ - spread::SpreadAllocate, - SpreadLayout, -}; -use ink_primitives::Key; - -/// Types that can be default initialized to a single storage cell. -pub trait PackedAllocate: SpreadAllocate + PackedLayout { - /// Indicates to `self` that is has just been allocated to the storage. - /// - /// # Note - /// - /// Most types will have to implement a trivial forwarding to their fields. - fn allocate_packed(&mut self, at: &Key); -} - -/// Types that can be stored to and loaded from a single contract storage cell. -pub trait PackedLayout: SpreadLayout + scale::Encode + scale::Decode { - /// Indicates to `self` that is has just been pulled from the storage. - /// - /// # Note - /// - /// Most types will have to implement a trivial forwarding to their fields. - fn pull_packed(&mut self, at: &Key); - - /// Indicates to `self` that it is about to be pushed to contract storage. - /// - /// # Note - /// - /// Most types will have to implement a trivial forwarding to their fields. - fn push_packed(&self, at: &Key); - - /// Indicates to `self` that it is about to be cleared from contract storage. - /// - /// # Note - /// - /// Most types will have to implement a trivial forwarding to their fields. - fn clear_packed(&self, at: &Key); -} diff --git a/crates/storage/src/traits/pull_or_init.rs b/crates/storage/src/traits/pull_or_init.rs new file mode 100644 index 00000000000..e14289a786f --- /dev/null +++ b/crates/storage/src/traits/pull_or_init.rs @@ -0,0 +1,111 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::traits::{ + pull_storage, + storage::OnCallInitializer, + DecodeWrapper, + Storable, +}; +use ink_primitives::Key; + +pub struct PullOrInit { + marker: core::marker::PhantomData T>, +} + +impl PullOrInit { + #[allow(dead_code)] + pub fn pull_or_init(key: &Key) -> T { + let maybe_instance = ink_env::get_contract_storage::>(key); + match maybe_instance { + Ok(None) | Err(_) => { + let mut instance = Default::default(); + ::initialize(&mut instance); + instance + } + Ok(Some(wrapper)) => wrapper.0, + } + } +} + +pub trait PullOrInitFallback { + #[allow(dead_code)] + fn pull_or_init(key: &Key) -> T { + pull_storage(key) + } +} +impl PullOrInitFallback for PullOrInit {} + +/// Pulls the struct from the storage or creates and new one and inits it. +#[macro_export] +#[doc(hidden)] +macro_rules! pull_or_init { + ( $T:ty, $key:expr $(,)? ) => {{ + #[allow(unused_imports)] + use $crate::traits::pull_or_init::PullOrInitFallback as _; + + $crate::traits::pull_or_init::PullOrInit::<$T>::pull_or_init(&$key) + }}; +} + +#[cfg(test)] +mod tests { + use crate::traits::{ + push_storage, + OnCallInitializer, + }; + use ink_primitives::Key; + + #[derive(Default, scale::Encode, scale::Decode)] + struct U32(u32); + + impl OnCallInitializer for U32 { + fn initialize(&mut self) { + self.0 = 123; + } + } + + #[ink_lang::test] + fn init_works() { + const KEY: Key = 111; + let instance = pull_or_init!(U32, KEY); + assert_eq!(123, instance.0); + } + + #[ink_lang::test] + fn pull_or_init_works() { + const KEY: Key = 111; + push_storage(&U32(456), &KEY); + let instance = pull_or_init!(U32, KEY); + + // Instead of init we used a pulled value + assert_eq!(456, instance.0); + } + + #[ink_lang::test] + #[should_panic(expected = "storage entry was empty")] + fn pull_or_init_fails() { + const KEY: Key = 111; + let instance = pull_or_init!(u32, KEY); + assert_eq!(123, instance); + } + + #[ink_lang::test] + fn pull_works() { + const KEY: Key = 111; + push_storage(&321, &KEY); + let instance = pull_or_init!(u32, KEY); + assert_eq!(321, instance); + } +} diff --git a/crates/storage/src/traits/spread.rs b/crates/storage/src/traits/spread.rs deleted file mode 100644 index a1bcee550f6..00000000000 --- a/crates/storage/src/traits/spread.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use super::KeyPtr; - -/// This constant is used by some types to make sure that cleaning up -/// behind them won't become way too expensive. Since we are missing -/// Substrate's storage bulk removal feature we cannot do better than -/// this at the moment. -/// The number is arbitrarily chosen. Might need adjustments later. -pub const FOOTPRINT_CLEANUP_THRESHOLD: u64 = 32; - -/// Types that can be default initialized to some area of the contract storage. -pub trait SpreadAllocate: SpreadLayout { - /// Default initializes the implementing type using spread layout. - /// - /// # Note - /// - /// - The key pointer denotes the position in contract storage where - /// the instance is being allocated at. - /// - Fields of `Self` are allocated in order and construct `Self` upon - /// completion. - fn allocate_spread(ptr: &mut KeyPtr) -> Self; -} - -/// Types that can be stored to and loaded from the contract storage. -pub trait SpreadLayout { - /// The footprint of the type. - /// - /// This is the number of adjunctive cells the type requires in order to - /// be stored in the contract storage with spread layout. - /// - /// # Examples - /// - /// An instance of type `i32` requires one storage cell, so its footprint is 1. - /// An instance of type `(i32, i32)` requires 2 storage cells since a - /// tuple or any other combined data structure always associates disjunctive - /// cells for its sub types. The same applies to arrays, e.g. `[i32; 5]` - /// has a footprint of 5. - const FOOTPRINT: u64; - - /// Indicates whether a type requires deep clean-up of its state meaning that - /// a clean-up routine has to decode an entity into an instance in order to - /// eventually recurse upon its tear-down. - /// This is not required for the majority of primitive data types such as `i32`, - /// however types such as `storage::Box` that might want to forward the clean-up - /// procedure to their inner `T` require a deep clean-up. - /// - /// # Note - /// - /// The default is set to `true` in order to have correctness by default since - /// no type invariants break if a deep clean-up is performed on a type that does - /// not need it but performing a shallow clean-up for a type that requires a - /// deep clean-up would break invariants. - /// This is solely a setting to improve performance upon clean-up for some types. - const REQUIRES_DEEP_CLEAN_UP: bool = true; - - /// Pulls an instance of `Self` from the contract storage. - /// - /// The key pointer denotes the position where the instance is being pulled - /// from within the contract storage - /// - /// # Note - /// - /// This method of pulling is depth-first: Sub-types are pulled first and - /// construct the super-type through this procedure. - fn pull_spread(ptr: &mut KeyPtr) -> Self; - - /// Pushes an instance of `Self` to the contract storage. - /// - /// - Tries to spread `Self` to as many storage cells as possible. - /// - The key pointer denotes the position where the instance is being pushed - /// to the contract storage. - /// - /// # Note - /// - /// This method of pushing is depth-first: Sub-types are pushed before - /// their parent or super type. - fn push_spread(&self, ptr: &mut KeyPtr); - - /// Clears an instance of `Self` from the contract storage. - /// - /// - Tries to clean `Self` from contract storage as if `self` was stored - /// in it using spread layout. - /// - The key pointer denotes the position where the instance is being cleared - /// from the contract storage. - /// - /// # Note - /// - /// This method of clearing is depth-first: Sub-types are cleared before - /// their parent or super type. - fn clear_spread(&self, ptr: &mut KeyPtr); -} diff --git a/crates/storage/src/traits/storage.rs b/crates/storage/src/traits/storage.rs new file mode 100644 index 00000000000..0ae594a60d8 --- /dev/null +++ b/crates/storage/src/traits/storage.rs @@ -0,0 +1,97 @@ +// Copyright 2018-2022 Parity Technologies (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ink_primitives::Key; + +/// Types that implement `scale::Encode` and `scale::Decode` called - **Packed**. Those types +/// support serialization and deserialization into/from storage and occupy only one storage cell. +/// +/// All other types - **Non-Packed**. +/// +/// # Note +/// +/// The trait is automatically implemented for types that implement `scale::Encode` +/// and `scale::Decode` via blank implementation. +/// +/// Don't try to implement that trait manually. +pub trait Packed: scale::Decode + scale::Encode {} + +/// Every type that wants to be a part of the storage should implement this trait. +/// The trait is used for serialization/deserialization into/from storage. +/// +/// # Note +/// +/// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types +/// via blank implementation. +pub trait Storable: Sized { + /// Convert self to a slice and append it to the destination. + fn encode(&self, dest: &mut T); + + /// Attempt to deserialize the value from input. + fn decode(input: &mut I) -> Result; +} + +/// Returns storage key for the type +/// +/// # Note +/// +/// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types +/// via blank implementation. +pub trait KeyHolder { + /// Storage key of the type + const KEY: Key; + + /// Returns the storage key. + fn key(&self) -> Key { + Self::KEY + } +} + +/// Describes the type that should be used for storing the value and preferred storage key. +/// +/// # Note +/// +/// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types +/// via blank implementation. +pub trait Item { + /// Type with storage key inside + type Type: Storable; + /// Preferred storage key + type PreferredKey: KeyHolder; +} + +/// Automatically returns the type that should be used for storing the value. +/// +/// Trait is used be codegen to use the right storage type. +pub trait AutoItem { + /// Type with storage key inside + type Type: Storable; +} + +/// The contract can implement that trait to support initialization on the runtime +/// if it is unable to pull from the storage. +/// +/// It can be in several cases: +/// - The contract doesn't have constructor. That initializer can be alternative for the constructor. +/// - The constructor was not called due to upgrade ability, `Proxy` or `Diamond` pattern. +/// - The storage was moved or corrupted. +/// +/// If the trait is not implemented the behavior of the storage is default. +/// It should be first initialized by the constructor. +pub trait OnCallInitializer: Default { + /// `Default::default` creates the instance of the contract. + /// After the `initialize` method is called on that instance. + /// The developer can do everything that he wants during initialization or do nothing. + fn initialize(&mut self); +} diff --git a/examples/delegator/lib.rs b/examples/delegator/lib.rs index df399425f31..6f3f4dc3ca4 100644 --- a/examples/delegator/lib.rs +++ b/examples/delegator/lib.rs @@ -6,10 +6,6 @@ use ink_lang as ink; mod delegator { use accumulator::AccumulatorRef; use adder::AdderRef; - use ink_storage::traits::{ - PackedLayout, - SpreadLayout, - }; use subber::SubberRef; /// Specifies the state of the `delegator` contract. @@ -18,20 +14,10 @@ mod delegator { /// and in `Subber` state will delegate to the `Subber` contract. /// /// The initial state is `Adder`. - #[derive( - Debug, - Copy, - Clone, - PartialEq, - Eq, - scale::Encode, - scale::Decode, - SpreadLayout, - PackedLayout, - )] + #[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", - derive(::scale_info::TypeInfo, ::ink_storage::traits::StorageLayout) + derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) )] pub enum Which { Adder, diff --git a/examples/dns/lib.rs b/examples/dns/lib.rs index 7a389da1c0f..4da715a97ac 100644 --- a/examples/dns/lib.rs +++ b/examples/dns/lib.rs @@ -4,10 +4,7 @@ use ink_lang as ink; #[ink::contract] mod dns { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; /// Emitted whenever a new name is being registered. #[ink(event)] @@ -58,7 +55,7 @@ mod dns { /// to facilitate transfers, voting and DApp-related operations instead /// of resorting to long IP addresses that are hard to remember. #[ink(storage)] - #[derive(Default, SpreadAllocate)] + #[derive(Default)] pub struct DomainNameService { /// A hashmap to store all name to addresses mapping. name_to_address: Mapping, @@ -85,11 +82,7 @@ mod dns { /// Creates a new domain name service contract. #[ink(constructor)] pub fn new() -> Self { - // This call is required in order to correctly initialize the - // `Mapping`s of our contract. - ink_lang::utils::initialize_contract(|contract: &mut Self| { - contract.default_address = Default::default(); - }) + Default::default() } /// Register specific name with caller as owner. @@ -115,7 +108,7 @@ mod dns { return Err(Error::CallerIsNotOwner) } - let old_address = self.name_to_address.get(name); + let old_address = self.name_to_address.get(&name); self.name_to_address.insert(&name, &new_address); self.env().emit_event(SetAddress { diff --git a/examples/erc1155/lib.rs b/examples/erc1155/lib.rs index ccaa91bbe29..f15981fc871 100644 --- a/examples/erc1155/lib.rs +++ b/examples/erc1155/lib.rs @@ -187,10 +187,7 @@ pub trait Erc1155TokenReceiver { mod erc1155 { use super::*; - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; type Owner = AccountId; type Operator = AccountId; @@ -230,7 +227,7 @@ mod erc1155 { /// An ERC-1155 contract. #[ink(storage)] - #[derive(Default, SpreadAllocate)] + #[derive(Default)] pub struct Contract { /// Tracks the balances of accounts across the different tokens that they might be holding. balances: Mapping<(AccountId, TokenId), Balance>, @@ -245,11 +242,7 @@ mod erc1155 { /// Initialize a default instance of this ERC-1155 implementation. #[ink(constructor)] pub fn new() -> Self { - // This call is required in order to correctly initialize the - // `Mapping`s of our contract. - // - // Not that `token_id_nonce` will be initialized to its `Default` value. - ink_lang::utils::initialize_contract(|_| {}) + Default::default() } /// Create the initial supply for a token. diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index a0dc1da4f2b..c1e3d6eb9ff 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -4,14 +4,11 @@ use ink_lang as ink; #[ink::contract] mod erc20 { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; /// A simple ERC-20 contract. #[ink(storage)] - #[derive(SpreadAllocate)] + #[derive(Default)] pub struct Erc20 { /// Total token supply. total_supply: Balance, @@ -60,23 +57,16 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(initial_supply: Balance) -> Self { - // This call is required in order to correctly initialize the - // `Mapping`s of our contract. - ink_lang::utils::initialize_contract(|contract| { - Self::new_init(contract, initial_supply) - }) - } - - /// Default initializes the ERC-20 contract with the specified initial supply. - fn new_init(&mut self, initial_supply: Balance) { + let mut instance = Erc20::default(); let caller = Self::env().caller(); - self.balances.insert(&caller, &initial_supply); - self.total_supply = initial_supply; + instance.balances.insert(&caller, &initial_supply); + instance.total_supply = initial_supply; Self::env().emit_event(Transfer { from: None, to: Some(caller), value: initial_supply, }); + instance } /// Returns the total token supply. diff --git a/examples/erc721/lib.rs b/examples/erc721/lib.rs index 0bc88fad932..da3d3ebee48 100644 --- a/examples/erc721/lib.rs +++ b/examples/erc721/lib.rs @@ -54,10 +54,7 @@ use ink_lang as ink; #[ink::contract] mod erc721 { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; use scale::{ Decode, @@ -68,7 +65,7 @@ mod erc721 { pub type TokenId = u32; #[ink(storage)] - #[derive(Default, SpreadAllocate)] + #[derive(Default)] pub struct Erc721 { /// Mapping from token to owner. token_owner: Mapping, @@ -129,9 +126,7 @@ mod erc721 { /// Creates a new ERC-721 token contract. #[ink(constructor)] pub fn new() -> Self { - // This call is required in order to correctly initialize the - // `Mapping`s of our contract. - ink_lang::utils::initialize_contract(|_| {}) + Default::default() } /// Returns the balance of the owner. diff --git a/examples/mother/Cargo.toml b/examples/mother/Cargo.toml index acf7a85dc24..79ce2b15465 100755 --- a/examples/mother/Cargo.toml +++ b/examples/mother/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "mother" description = "Mother of all contracts" -version = "3.0.1" +version = "4.0.0" authors = ["Parity Technologies "] edition = "2021" publish = false diff --git a/examples/mother/lib.rs b/examples/mother/lib.rs index a6cb99ece3e..d3605ad0524 100755 --- a/examples/mother/lib.rs +++ b/examples/mother/lib.rs @@ -29,52 +29,23 @@ mod mother { vec::Vec, }; - use ink_lang::utils::initialize_contract; - use ink_storage::{ - traits::{ - PackedLayout, - SpreadAllocate, - SpreadLayout, - }, - Mapping, - }; + use ink_storage::Mapping; - use ink_storage::traits::KeyPtr; /// Struct for storing winning bids per bidding sample (a block). /// Vector index corresponds to sample number. /// Wrapping vector, just added for testing UI components. - #[derive( - Default, - scale::Encode, - scale::Decode, - PartialEq, - Eq, - Debug, - Clone, - SpreadLayout, - PackedLayout, - SpreadAllocate, - )] + #[derive(Default, PartialEq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", - derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,) + derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) )] pub struct Bids(Vec>>); /// Auction outline. - #[derive( - scale::Encode, - scale::Decode, - Eq, - PartialEq, - Debug, - Clone, - SpreadLayout, - PackedLayout, - )] + #[derive(PartialEq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", - derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,) + derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) )] pub enum Outline { NoWinner, @@ -85,19 +56,10 @@ mod mother { /// Auction statuses. /// Logic inspired by /// [Parachain Auction](https://github.com/paritytech/polkadot/blob/master/runtime/common/src/traits.rs#L160) - #[derive( - scale::Encode, - scale::Decode, - Eq, - PartialEq, - Debug, - Clone, - SpreadLayout, - PackedLayout, - )] + #[derive(PartialEq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", - derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,) + derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) )] pub enum Status { /// An auction has not started yet. @@ -115,27 +77,11 @@ mod mother { RfDelay(BlockNumber), } - impl SpreadAllocate for Status { - #[inline] - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - ptr.advance_by(::FOOTPRINT * 2); - Self::NotStarted - } - } /// Struct for storing auction data. - #[derive( - Debug, - PartialEq, - scale::Encode, - scale::Decode, - Clone, - SpreadLayout, - PackedLayout, - SpreadAllocate, - )] + #[derive(Debug, PartialEq, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", - derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout,) + derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) )] pub struct Auction { /// Branded name of the auction event. @@ -186,7 +132,7 @@ mod mother { /// Storage of the contract. #[ink(storage)] - #[derive(Default, SpreadAllocate)] + #[derive(Default)] pub struct Mother { auction: Auction, balances: Mapping, @@ -195,18 +141,15 @@ mod mother { impl Mother { #[ink(constructor)] pub fn new(auction: Auction) -> Self { - initialize_contract(|c: &mut Self| { - c.balances = >::default(); - c.auction = auction; - }) + Self { + balances: Default::default(), + auction, + } } #[ink(constructor)] pub fn default() -> Self { - initialize_contract(|c: &mut Self| { - c.balances = >::default(); - c.auction = Auction::default(); - }) + Default::default() } /// Takes an auction data struct as input and returns it back. diff --git a/examples/multisig/lib.rs b/examples/multisig/lib.rs index 43c45ee20e4..5bd6eaf164d 100755 --- a/examples/multisig/lib.rs +++ b/examples/multisig/lib.rs @@ -70,14 +70,7 @@ mod multisig { ExecutionInput, }; use ink_prelude::vec::Vec; - use ink_storage::{ - traits::{ - PackedLayout, - SpreadAllocate, - SpreadLayout, - }, - Mapping, - }; + use ink_storage::Mapping; use scale::Output; /// Tune this to your liking but be wary that allowing too many owners will not perform well. @@ -99,7 +92,7 @@ mod multisig { } /// Indicates whether a transaction is already confirmed or needs further confirmations. - #[derive(scale::Encode, scale::Decode, Clone, Copy, SpreadLayout, PackedLayout)] + #[derive(Clone, Copy, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) @@ -113,7 +106,7 @@ mod multisig { /// A Transaction is what every `owner` can submit for confirmation by other owners. /// If enough owners agree it will be executed by the contract. - #[derive(scale::Encode, scale::Decode, SpreadLayout, PackedLayout)] + #[derive(scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive( @@ -147,9 +140,7 @@ mod multisig { /// This is a book keeping struct that stores a list of all transaction ids and /// also the next id to use. We need it for cleaning up the storage. - #[derive( - scale::Encode, scale::Decode, SpreadLayout, PackedLayout, SpreadAllocate, Default, - )] + #[derive(Default, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive( @@ -247,7 +238,7 @@ mod multisig { } #[ink(storage)] - #[derive(SpreadAllocate)] + #[derive(Default)] pub struct Multisig { /// Every entry in this map represents the confirmation of an owner for a /// transaction. This is effectively a set rather than a map. @@ -281,19 +272,19 @@ mod multisig { /// If `requirement` violates our invariant. #[ink(constructor)] pub fn new(requirement: u32, mut owners: Vec) -> Self { - ink_lang::utils::initialize_contract(|contract: &mut Self| { - owners.sort_unstable(); - owners.dedup(); - ensure_requirement_is_valid(owners.len() as u32, requirement); + let mut contract = Multisig::default(); + owners.sort_unstable(); + owners.dedup(); + ensure_requirement_is_valid(owners.len() as u32, requirement); - for owner in &owners { - contract.is_owner.insert(owner, &()); - } + for owner in &owners { + contract.is_owner.insert(owner, &()); + } - contract.owners = owners; - contract.transaction_list = Default::default(); - contract.requirement = requirement; - }) + contract.owners = owners; + contract.transaction_list = Default::default(); + contract.requirement = requirement; + contract } /// Add a new owner to the contract. diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index a3c8ec12ec3..bd7715c1afc 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -5,10 +5,7 @@ use ink_lang as ink; #[ink::contract] mod erc20 { use ink_lang as ink; - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; + use ink_storage::Mapping; /// The ERC-20 error types. #[derive(Debug, PartialEq, Eq, scale::Encode, scale::Decode)] @@ -59,7 +56,7 @@ mod erc20 { /// A simple ERC-20 contract. #[ink(storage)] - #[derive(SpreadAllocate)] + #[derive(Default)] pub struct Erc20 { /// Total token supply. total_supply: Balance, @@ -97,9 +94,9 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(initial_supply: Balance) -> Self { - ink_lang::utils::initialize_contract(|contract| { - Self::new_init(contract, initial_supply) - }) + let mut instance = Self::default(); + instance.new_init(initial_supply); + instance } /// Default initializes the ERC-20 contract with the specified initial supply. diff --git a/examples/upgradeable-contracts/delegate-calls/lib.rs b/examples/upgradeable-contracts/delegate-calls/lib.rs index d4eb29d6869..be0a28c0af8 100644 --- a/examples/upgradeable-contracts/delegate-calls/lib.rs +++ b/examples/upgradeable-contracts/delegate-calls/lib.rs @@ -21,19 +21,24 @@ pub mod upgradeable_contract { use ink_env::call::DelegateCall; use ink_primitives::{ Key, - KeyPtr, + KeyComposer, }; - use ink_storage::traits::SpreadLayout; + use ink_storage::traits::{ + KeyHolder, + ManualKey, + }; + + const PROXY_KEY: Key = KeyComposer::from_str("ProxyFields"); - /// This struct contains the data related to the Proxy storage. + /// A simple proxy contract. + /// + /// The proxy contracts is stored in own storage cell under the `PROXY_KEY` + /// instead of the default contract storage key = `0`. /// - /// The reason this is a separate structure is that we want to keep - /// the data for this contract in a separate place (as in the implementation - /// of [`SpreadLayout`](ink_storage::traits::SpreadLayout)), so that it does not get - /// overwritten by any contract upgrade, which might introduce storage changes. - #[derive(Debug)] - #[cfg_attr(feature = "std", derive(ink_storage::traits::StorageLayout))] - struct ProxyFields { + /// This allows us to store the proxy contract's storage in such a way that it will not + /// conflict with the the default storage layout of the contract we're proxying calls to. + #[ink(storage)] + pub struct Proxy> { /// The `Hash` of a contract code where any call that does not match a /// selector of this contract is forward to. forward_to: Hash, @@ -43,44 +48,6 @@ pub mod upgradeable_contract { admin: AccountId, } - const PROXY_FIELDS_STORAGE_KEY: [u8; 32] = ink_lang::blake2x256!("ProxyFields"); - - /// `SpreadLayout` is implemented manually to use its own `PROXY_FIELDS_STORAGE_KEY` - /// storage key instead of the default contract storage `ContractRootKey::ROOT_KEY`. - /// - /// This allows us to store the proxy contract's storage in such a way that it will not - /// conflict with the the default storage layout of the contract we're proxying calls to. - impl SpreadLayout for ProxyFields { - const FOOTPRINT: u64 = - ::FOOTPRINT + ::FOOTPRINT; - - fn pull_spread(_: &mut KeyPtr) -> Self { - let mut ptr = KeyPtr::from(Key::from(PROXY_FIELDS_STORAGE_KEY)); - Self { - forward_to: SpreadLayout::pull_spread(&mut ptr), - admin: SpreadLayout::pull_spread(&mut ptr), - } - } - - fn push_spread(&self, _: &mut KeyPtr) { - let mut ptr = KeyPtr::from(Key::from(PROXY_FIELDS_STORAGE_KEY)); - SpreadLayout::push_spread(&self.forward_to, &mut ptr); - SpreadLayout::push_spread(&self.admin, &mut ptr); - } - - fn clear_spread(&self, _: &mut KeyPtr) { - let mut ptr = KeyPtr::from(Key::from(PROXY_FIELDS_STORAGE_KEY)); - SpreadLayout::clear_spread(&self.forward_to, &mut ptr); - SpreadLayout::clear_spread(&self.admin, &mut ptr); - } - } - - /// A simple proxy contract. - #[ink(storage)] - pub struct Proxy { - proxy: ProxyFields, - } - impl Proxy { /// Instantiate this contract with an address of the `logic` contract. /// @@ -89,10 +56,8 @@ pub mod upgradeable_contract { #[ink(constructor)] pub fn new(forward_to: Hash) -> Self { Self { - proxy: ProxyFields { - forward_to, - admin: Self::env().caller(), - }, + forward_to, + admin: Self::env().caller(), } } @@ -102,12 +67,12 @@ pub mod upgradeable_contract { pub fn change_delegate_code(&mut self, new_code_hash: Hash) { assert_eq!( self.env().caller(), - self.proxy.admin, + self.admin, "caller {:?} does not have sufficient permissions, only {:?} does", self.env().caller(), - self.proxy.admin, + self.admin, ); - self.proxy.forward_to = new_code_hash; + self.forward_to = new_code_hash; } /// Fallback message for a contract call that doesn't match any @@ -123,7 +88,7 @@ pub mod upgradeable_contract { #[ink(message, payable, selector = _)] pub fn forward(&self) -> u32 { ink_env::call::build_call::() - .call_type(DelegateCall::new().code_hash(self.proxy.forward_to)) + .call_type(DelegateCall::new().code_hash(self.forward_to)) .call_flags( ink_env::CallFlags::default() // We don't plan to use the input data after the delegated call, so the @@ -137,7 +102,7 @@ pub mod upgradeable_contract { .unwrap_or_else(|err| { panic!( "delegate call to {:?} failed due to {:?}", - self.proxy.forward_to, err + self.forward_to, err ) }); unreachable!( diff --git a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs index 17cd695a7a2..fc23928c5cc 100644 --- a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs +++ b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs @@ -4,52 +4,48 @@ #![cfg_attr(not(feature = "std"), no_std)] -mod upgradeable; - use ink_lang as ink; #[ink::contract] pub mod flipper { - use crate::upgradeable::{ - NotInitialized, - Upgradeable, - }; + use ink_storage::traits::OnCallInitializer; + /// The `Flipper` doesn't use the manual storage key. + /// That means that it is stored under the default zero storage key. #[ink(storage)] + #[derive(Default)] pub struct Flipper { - /// The field is `Upgradeable`, which means if the field is not initialized, it will be. - /// - /// By default ink! would throw an error that the field is not initialized. - /// With that wrapper, you can initialize the field later during the method execution, - /// not in the constructor. - value: Upgradeable, + value: bool, + } + + /// By default ink! would throw an error that the field is not initialized. + /// But if the contract implements `ink_storage::traits::OnCallInitializer`, then it will + /// be initialized later in the `OnCallInitializer::initialize` during the method execution, + /// not in the constructor. + impl OnCallInitializer for Flipper { + fn initialize(&mut self) { + // Let's initialize it with `false` by default + self.value = false; + } } impl Flipper { /// Creates a new flipper smart contract initialized with the given value. #[ink(constructor)] pub fn new(init_value: bool) -> Self { - Self { - value: Upgradeable::new(init_value), - } - } - - /// Creates a new flipper smart contract initialized to `false`. - #[ink(constructor)] - pub fn default() -> Self { - Self::new(Default::default()) + Self { value: init_value } } /// Flips the current value of the Flipper's boolean. #[ink(message)] pub fn flip(&mut self) { - *self.value = !*self.value; + self.value = !self.value; } /// Returns the current value of the Flipper's boolean. #[ink(message)] pub fn get(&self) -> bool { - *self.value + self.value } } diff --git a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/upgradeable.rs b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/upgradeable.rs deleted file mode 100644 index ed422189005..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/upgradeable.rs +++ /dev/null @@ -1,178 +0,0 @@ -use core::marker::PhantomData; -use ink_primitives::{ - Key, - KeyPtr, -}; -use ink_storage::traits::{ - PackedAllocate, - PackedLayout, - SpreadAllocate, - SpreadLayout, -}; -use scale::{ - Decode, - Encode, -}; - -/// It is a status struct for `Upgradeable`, to specify that the inner type is initialized. -#[derive(Debug)] -pub struct Initialized; - -/// It is a status struct for `Upgradeable`, to specify that the inner type may be not -/// initialized and `pull_spread` should initialize it. -#[derive(Debug)] -pub struct NotInitialized; - -/// The `Upgradeable` means if the field is not initialized, it will be. -/// -/// By default ink! would throw an error that the field is not initialized. -/// With that wrapper, you can initialize the field later during the method execution, -/// not in the constructor. It can be done because `SpreadLayout` for -/// `Upgradeable` creates the object, if storage key is empty. -#[derive(Debug, Decode, Encode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub struct Upgradeable { - inner: T, - status: PhantomData InitializationStatus>, -} - -impl Upgradeable { - pub fn new(inner: T) -> Self { - Upgradeable { - inner, - status: Default::default(), - } - } -} - -/// It is default implementation of `SpreadLayout` for case when we don't need to init. -impl SpreadLayout for Upgradeable { - const FOOTPRINT: u64 = T::FOOTPRINT; - const REQUIRES_DEEP_CLEAN_UP: bool = T::REQUIRES_DEEP_CLEAN_UP; - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - Upgradeable::new(T::pull_spread(ptr)) - } - - fn push_spread(&self, ptr: &mut KeyPtr) { - T::push_spread(&self.inner, ptr) - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - T::clear_spread(&self.inner, ptr) - } -} - -/// It is implementation of `SpreadLayout` that initialize the inner type if it is not initialized. -impl SpreadLayout for Upgradeable { - const FOOTPRINT: u64 = ::FOOTPRINT; - const REQUIRES_DEEP_CLEAN_UP: bool = ::REQUIRES_DEEP_CLEAN_UP; - - fn pull_spread(ptr: &mut KeyPtr) -> Self { - if ink_env::get_contract_storage::(ptr.advance_by(0)) - .expect("could not properly decode storage entry") - .is_none() - { - ::allocate_spread(ptr) - } else { - Upgradeable::new(::pull_spread(ptr)) - } - } - - fn push_spread(&self, ptr: &mut KeyPtr) { - ::push_spread(&self.inner, ptr) - } - - fn clear_spread(&self, ptr: &mut KeyPtr) { - ::clear_spread(&self.inner, ptr) - } -} - -/// Below the boilerplate code to implement `PackedLayout`, `SpreadAllocate`, `PackedAllocate`. - -impl PackedLayout for Upgradeable { - fn pull_packed(&mut self, at: &Key) { - ::pull_packed(&mut self.inner, at) - } - - fn push_packed(&self, at: &Key) { - ::push_packed(&self.inner, at) - } - - fn clear_packed(&self, at: &Key) { - ::clear_packed(&self.inner, at) - } -} - -impl PackedLayout for Upgradeable { - fn pull_packed(&mut self, at: &Key) { - ::pull_packed(&mut self.inner, at) - } - - fn push_packed(&self, at: &Key) { - ::push_packed(&self.inner, at) - } - - fn clear_packed(&self, at: &Key) { - ::clear_packed(&self.inner, at) - } -} - -impl SpreadAllocate for Upgradeable { - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - Upgradeable::new(::allocate_spread(ptr)) - } -} - -impl SpreadAllocate for Upgradeable { - fn allocate_spread(ptr: &mut KeyPtr) -> Self { - Upgradeable::new(::allocate_spread(ptr)) - } -} - -impl PackedAllocate for Upgradeable { - fn allocate_packed(&mut self, at: &Key) { - ::allocate_packed(&mut self.inner, at) - } -} - -impl PackedAllocate for Upgradeable { - fn allocate_packed(&mut self, at: &Key) { - ::allocate_packed(&mut self.inner, at) - } -} - -impl core::ops::Deref for Upgradeable { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl core::ops::DerefMut for Upgradeable { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl Default for Upgradeable { - fn default() -> Self { - Self::new(Default::default()) - } -} - -#[cfg(feature = "std")] -const _: () = { - use ink_metadata::layout::Layout; - use ink_storage::traits::StorageLayout; - - impl StorageLayout for Upgradeable - where - T: PackedLayout + StorageLayout + scale_info::TypeInfo + 'static, - { - fn layout(key_ptr: &mut KeyPtr) -> Layout { - ::layout(key_ptr) - } - } -}; diff --git a/examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml b/examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml index 2406c0f44e6..17070657438 100644 --- a/examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml +++ b/examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml @@ -10,7 +10,7 @@ publish = false [dependencies] ink_primitives = { path = "../../../../crates/primitives", default-features = false } ink_metadata = { path = "../../../../crates/metadata", default-features = false, features = ["derive"], optional = true } -ink_env = { path = "../../../../crates/env", default-features = false, features = ["ink-debug"] } +ink_env = { path = "../../../../crates/env", default-features = false } ink_storage = { path = "../../../../crates/storage", default-features = false } ink_lang = { path = "../../../../crates/lang", default-features = false }