From 2b26752dc38272e26e35393dc389c8f4b8fd1a6f Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 11:54:18 +0100 Subject: [PATCH 01/40] Import new unstable functions with transparent hashing. Updated the API to work with the generic key `K: Encode` instead of the old `Key`. Also, the change contains optimization to reduce the size of contracts. In most cases, it is `#[inline(always)]`; but `return_value` also got small optimization; removed usage of `extract_from_slice` where it is not needed. --- 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 + 8 files changed, 176 insertions(+), 68 deletions(-) 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, }, From 8dbf040b64eb18be25effea402a778811a0070d7 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 12:30:32 +0100 Subject: [PATCH 02/40] primitives crate: Removed old 32 bytes `Key`. Replaced it with `u32`. Added `KeyComposer`, it is a helper struct that does all manipulation with the storage key. It allows concat two storage keys into one, compute storage key for fields based on the filed, struct, enum, variants names. Removed all tests and benches. Didn't add it for new primitives because the key is standard `u32` and all keys are calculated during compilation. storage crate: Removed `SpreadLayout`, `PackedLayout`, `SpreadAllocate`, `PackedAllocate`, and all related helper functions. Removed `Packed` struct cause it is the default behavior for storage right now. Added `Lazy` struct that allows `get`/`set` value from/into the storage. It is similar to `Mapping` but works with one storage key and one value. Introduced new main traits to work with storage in `storage/src/traits/storage.rs`. Also added a new `OnCallInitializer` trait to improve the flow with upgradable contracts and support initialization on demand by default. Added `pull_or_init` macro that inits the storage struct if it is impossible to load it. It also can be used as optimization for contracts without an explicit constructor. Replaced implementation of old traits for main primitives with a new one. Added blanket implementation of new traits for structures that are `Packed` by default. It reduces the amount of code and adds support of generic structures but adds problems with tuples(now tuples implement new traits only if inner items are `Packed`). Introduced `AutoKey` and `ManualKey` that allows specifying which key the user wants to use. Added support of it into all traits and structures. Refactored `Mapping` to follow new rules. --- .config/cargo_spellcheck.dic | 1 + 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/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 +++ 28 files changed, 946 insertions(+), 3136 deletions(-) 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 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 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/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/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); +} From 341c48b965ee4afab580a2eb74a83763201446f3 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 14:24:25 +0100 Subject: [PATCH 03/40] metadata crate: Updated storage layout in the metadata. Introduces the concept of roots and leafs. Root defines the storage key for all sub-tree until there will be another Root. Leafs are common types that are part of the sub-tree and serialized/deserialized together into one storage key. Replaced 32 bytes storage key with `u32`. Added validation that all root storage keys don't overlap. Maybe better to add that error or reuse that validator on the `cargo-contract` side to show a more user-friendly error. `RootLayout` and validator are used in codegen(next commit). The contract is wrapped into `RootLayout`, and we do validation for that tree. Metadata now contains name for each struct/enum/variant/field. It is useful information because now the storage key is calculated based on the name of struct/enum/variant/field. storage crate: Added a new helper crate `storage/codegen`. It contains some useful functional that is used in `ink_storage_derive` and in `ink_lang_codegen` crates. It has a method that returns all types of the struct/enum/unit and a method that finds "salt" in the generics of the struct/enum. It uses magic constant "KeyHolder"(about that you can read in issue) to find salt, so I tried to have only one place where we are using that constant. Replaced derive implementation of old trait with new one. You can check the tests to see how the implementation looks like. `Storable` recursively call `encode` and `decode` for all fields. `KeyHolder` return key of the salt. `Item` uses `AutoKey` or key specified by the user. I want to highlight that `PreferredKey` only is used with the `AutoItem` trait. If `PreferredKey` is `AutoKey`, then `AutoItem` select auto-generated key, otherwise preferred. So `AutoItem` trait decides that key to use. It is why derive macro only set `PreferredKey`. Updated drive for `StorageLayout`, now it uses `u32` and pass name of the struct into metadata. --- crates/metadata/src/layout/mod.rs | 190 ++++- crates/metadata/src/layout/tests.rs | 249 +++--- crates/metadata/src/layout/validate.rs | 298 +++++++ 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 | 1 + 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 +++-- 24 files changed, 1885 insertions(+), 1958 deletions(-) create mode 100644 crates/metadata/src/layout/validate.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 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/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..4a75579c1a2 100644 --- a/crates/storage/derive/Cargo.toml +++ b/crates/storage/derive/Cargo.toml @@ -22,6 +22,7 @@ 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"] } 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), + ), + ] + ), ) }, ] From 346529c35e14649d2b6d7b52af81b5a66980a1cd Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 14:33:22 +0100 Subject: [PATCH 04/40] Removed `initialize_contract` and related to initialization stuff. Now the codegen uses `pull_or_init` in the `call`. Updated `execute_constructor` to work with new storage methods. Allowed usage of generics during the declaration of the primary contract storage. Users can specify the default storage key with `KeyHolder` via generic. Added parser for `storage_item` macro with its config. --- 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 +- 13 files changed, 355 insertions(+), 81 deletions(-) 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%) 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, }; From b731f92269fd49ffbcf920c1810268f449c60507 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 14:43:15 +0100 Subject: [PATCH 05/40] Removed the old codegen related to spread and packed layout. If some object implements `Decode` and `Encode`, it is `Packed`, and it uses the blanket implementation of new traits. In dispatch, codegen started to use a new method to work with storage. In metadata codegen added usage of new `RootLayout`. We wrap the contract into that layout because the contract has its storage key for all inner fields by default. Also added a run of validation logic during metadata generation. Added `storage_item` macro. It transforms all types from autokey into manual key(if types support it). It calculates the storage key based on the name(it uses the `KeyComposer::compute_key` function from the primitives crate). Also, macro generates an additional `Check` structure that includes all raw fields. It helps show correct errors to the user in case of typos, wrong types, etc. --- 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 + 12 files changed, 306 insertions(+), 184 deletions(-) create mode 100644 crates/lang/codegen/src/generator/storage_item.rs 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>; } From d6db8de2b9f54bd99af90fb09e407ecba74d4f05 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 14:45:20 +0100 Subject: [PATCH 06/40] Updated all examples to use a new API. Simplified delegate call example very well causes of new `OnCallInitializer` trait and support of manual specifying key. --- 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 +- 13 files changed, 97 insertions(+), 426 deletions(-) delete mode 100644 examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/upgradeable.rs 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 } From f967986d10688524570dbfff266eefa333415ecf Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 21 Jul 2022 14:46:29 +0100 Subject: [PATCH 07/40] UI tests for a new codegen. Can't highlight something unusual here=) --- 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 +- 27 files changed, 825 insertions(+), 32 deletions(-) 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 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 { | ^^^^^^^^^^^^^^^^^^^^^^^ From 8f511ec84133e6ac6bd6bb93792280c6212f5387 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 17:43:20 +0100 Subject: [PATCH 08/40] Apply all suggestion from the review --- .gitlab-ci.yml | 2 +- crates/env/src/api.rs | 5 +- crates/env/src/backend.rs | 5 +- crates/env/src/engine/off_chain/impls.rs | 10 +- crates/env/src/engine/on_chain/buffer.rs | 18 +- crates/env/src/engine/on_chain/ext.rs | 2 - crates/env/src/engine/on_chain/impls.rs | 12 +- crates/lang/codegen/src/generator/dispatch.rs | 4 +- crates/lang/codegen/src/generator/metadata.rs | 2 +- .../codegen/src/generator/storage_item.rs | 7 +- crates/lang/ir/src/ir/storage_item/mod.rs | 2 +- crates/lang/macro/src/lib.rs | 15 +- crates/lang/src/codegen/dispatch/execution.rs | 8 +- .../fail/argument_derive_invalid_type.rs | 2 +- .../fail/argument_derive_missing_arg.rs | 2 +- .../fail/collections_only_packed_1.stderr | 12 +- .../fail/collections_only_packed_2.stderr | 12 +- .../pass/argument_derive_false.rs | 8 +- .../pass/complex_non_packed_enum.rs | 16 +- .../pass/complex_non_packed_struct.rs | 4 +- .../storage_item/pass/complex_packed_enum.rs | 2 +- .../pass/complex_packed_struct.rs | 2 +- .../pass/default_storage_key_1.rs | 6 +- .../pass/default_storage_key_2.rs | 4 +- .../pass/default_storage_key_3.rs | 4 +- .../pass/default_storage_key_4.rs | 4 +- .../pass/non_packed_tuple_struct.rs | 2 +- .../storage_item/pass/packed_tuple_struct.rs | 2 +- crates/primitives/Cargo.toml | 2 + crates/primitives/derive/Cargo.toml | 31 ++ crates/primitives/derive/LICENSE | 203 ++++++++++++ crates/primitives/derive/README.md | 289 ++++++++++++++++++ crates/primitives/derive/src/lib.rs | 46 +++ .../derive/src/storable.rs | 16 +- crates/primitives/derive/src/tests/mod.rs | 47 +++ .../derive/src/tests/storable.rs | 98 +++--- crates/primitives/src/key.rs | 98 +++++- crates/primitives/src/lib.rs | 1 + crates/primitives/src/traits.rs | 39 +++ crates/storage/codegen/src/lib.rs | 5 +- crates/storage/derive/src/item.rs | 6 +- crates/storage/derive/src/key_holder.rs | 4 +- crates/storage/derive/src/lib.rs | 47 +-- crates/storage/derive/src/tests/item.rs | 24 +- crates/storage/derive/src/tests/key_holder.rs | 42 +-- crates/storage/derive/src/tests/mod.rs | 1 - crates/storage/src/lazy/mapping.rs | 63 ++-- crates/storage/src/lazy/mod.rs | 112 +++---- crates/storage/src/test_utils.rs | 2 +- crates/storage/src/traits/impls/arrays.rs | 24 -- crates/storage/src/traits/impls/mod.rs | 99 +++++- crates/storage/src/traits/impls/storage.rs | 107 ------- .../src/traits/impls/{prims.rs => tests.rs} | 34 ++- crates/storage/src/traits/impls/tuples.rs | 21 -- crates/storage/src/traits/layout/impls.rs | 2 +- crates/storage/src/traits/mod.rs | 49 +-- crates/storage/src/traits/pull_or_init.rs | 19 +- crates/storage/src/traits/storage.rs | 58 ++-- .../delegate-calls/lib.rs | 4 +- 59 files changed, 1211 insertions(+), 556 deletions(-) create mode 100644 crates/primitives/derive/Cargo.toml create mode 100644 crates/primitives/derive/LICENSE create mode 100644 crates/primitives/derive/README.md create mode 100644 crates/primitives/derive/src/lib.rs rename crates/{storage => primitives}/derive/src/storable.rs (87%) create mode 100644 crates/primitives/derive/src/tests/mod.rs rename crates/{storage => primitives}/derive/src/tests/storable.rs (79%) create mode 100644 crates/primitives/src/traits.rs delete mode 100644 crates/storage/src/traits/impls/arrays.rs delete mode 100644 crates/storage/src/traits/impls/storage.rs rename crates/storage/src/traits/impls/{prims.rs => tests.rs} (57%) delete mode 100644 crates/storage/src/traits/impls/tuples.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 17a12bb5602..cbeef8acb52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -261,7 +261,7 @@ docs: - cargo doc --no-deps --all-features -p scale-info -p ink_metadata -p ink_env -p ink_storage -p ink_storage_derive - -p ink_primitives -p ink_prelude + -p ink_primitives -p ink_prelude -p ink_primitives_derive -p ink_lang -p ink_lang_macro -p ink_lang_ir -p ink_lang_codegen - mv ${CARGO_TARGET_DIR}/doc ./crate-docs # FIXME: remove me after CI image gets nonroot diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 8796eac9ba0..1dc25fb38a7 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -39,6 +39,7 @@ use crate::{ Environment, Result, }; +use ink_primitives::traits::Storable; /// Returns the address of the caller of the executed contract. /// @@ -191,7 +192,7 @@ where pub fn set_contract_storage(key: &K, value: &V) -> Option where K: scale::Encode, - V: scale::Encode, + V: Storable, { ::on_instance(|instance| { EnvBackend::set_contract_storage::(instance, key, value) @@ -206,7 +207,7 @@ where pub fn get_contract_storage(key: &K) -> Result> where K: scale::Encode, - R: scale::Decode, + R: Storable, { ::on_instance(|instance| { EnvBackend::get_contract_storage::(instance, key) diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index edb1bb52243..755e2562d2c 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -27,6 +27,7 @@ use crate::{ Environment, Result, }; +use ink_primitives::traits::Storable; /// The flags to indicate further information about the end of a contract execution. #[derive(Default)] @@ -167,7 +168,7 @@ pub trait EnvBackend { fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where K: scale::Encode, - V: scale::Encode; + V: Storable; /// Returns the value stored under the given storage key in the contract's storage if any. /// @@ -177,7 +178,7 @@ pub trait EnvBackend { fn get_contract_storage(&mut self, key: &K) -> Result> where K: scale::Encode, - R: scale::Decode; + R: Storable; /// 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 diff --git a/crates/env/src/engine/off_chain/impls.rs b/crates/env/src/engine/off_chain/impls.rs index 9037014dfcf..8d53540fdc1 100644 --- a/crates/env/src/engine/off_chain/impls.rs +++ b/crates/env/src/engine/off_chain/impls.rs @@ -44,6 +44,7 @@ use ink_engine::{ ext, ext::Engine, }; +use ink_primitives::traits::Storable; /// The capacity of the static buffer. /// This is the same size as the ink! on-chain environment. We chose to use the same size @@ -186,16 +187,17 @@ impl EnvBackend for EnvInstance { fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where K: scale::Encode, - V: scale::Encode, + V: Storable, { - let v = scale::Encode::encode(value); + let mut v = vec![]; + Storable::encode(value, &mut v); self.engine.set_storage(&key.encode(), &v[..]) } fn get_contract_storage(&mut self, key: &K) -> Result> where K: scale::Encode, - R: scale::Decode, + R: Storable, { let mut output: [u8; 9600] = [0; 9600]; match self.engine.get_storage(&key.encode(), &mut &mut output[..]) { @@ -203,7 +205,7 @@ impl EnvBackend for EnvInstance { Err(ext::Error::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } - let decoded = scale::Decode::decode(&mut &output[..])?; + let decoded = Storable::decode(&mut &output[..])?; Ok(Some(decoded)) } diff --git a/crates/env/src/engine/on_chain/buffer.rs b/crates/env/src/engine/on_chain/buffer.rs index 29bdb72f198..ea5b46d97c6 100644 --- a/crates/env/src/engine/on_chain/buffer.rs +++ b/crates/env/src/engine/on_chain/buffer.rs @@ -164,7 +164,23 @@ impl<'a> ScopedBuffer<'a> { debug_assert_eq!(self.offset, 0); let buffer = core::mem::take(&mut self.buffer); let mut encode_scope = EncodeScope::from(buffer); - scale::Encode::encode_to(&value, &mut encode_scope); + scale::Encode::encode_to(value, &mut encode_scope); + let encode_len = encode_scope.len(); + let _ = core::mem::replace(&mut self.buffer, encode_scope.into_buffer()); + self.take(encode_len) + } + + /// Encode the given storable value into the scoped buffer and return the sub slice + /// containing all the encoded bytes. + #[inline(always)] + pub fn take_storable_encoded(&mut self, value: &T) -> &'a mut [u8] + where + T: ink_primitives::traits::Storable, + { + debug_assert_eq!(self.offset, 0); + let buffer = core::mem::take(&mut self.buffer); + let mut encode_scope = EncodeScope::from(buffer); + ink_primitives::traits::Storable::encode(value, &mut encode_scope); let encode_len = encode_scope.len(); let _ = core::mem::replace(&mut self.buffer, encode_scope.into_buffer()); self.take(encode_len) diff --git a/crates/env/src/engine/on_chain/ext.rs b/crates/env/src/engine/on_chain/ext.rs index 1993af329da..f38f301c6d2 100644 --- a/crates/env/src/engine/on_chain/ext.rs +++ b/crates/env/src/engine/on_chain/ext.rs @@ -645,8 +645,6 @@ macro_rules! impl_seal_wrapper_for { ) }; } - // 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` } )* } diff --git a/crates/env/src/engine/on_chain/impls.rs b/crates/env/src/engine/on_chain/impls.rs index 883b0fadd29..ee97116f890 100644 --- a/crates/env/src/engine/on_chain/impls.rs +++ b/crates/env/src/engine/on_chain/impls.rs @@ -14,7 +14,6 @@ use super::{ ext, - EncodeScope, EnvInstance, Error as ExtError, ScopedBuffer, @@ -47,6 +46,7 @@ use crate::{ ReturnFlags, TypedEnvBackend, }; +use ink_primitives::traits::Storable; impl CryptoHash for Blake2x128 { fn hash(input: &[u8], output: &mut ::Type) { @@ -226,18 +226,18 @@ impl EnvBackend for EnvInstance { fn set_contract_storage(&mut self, key: &K, value: &V) -> Option where K: scale::Encode, - V: scale::Encode, + V: Storable, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); - let value = buffer.take_encoded(value); + let value = buffer.take_storable_encoded(value); ext::set_storage(key, value) } fn get_contract_storage(&mut self, key: &K) -> Result> where K: scale::Encode, - R: scale::Decode, + R: Storable, { let mut buffer = self.scoped_buffer(); let key = buffer.take_encoded(key); @@ -247,7 +247,7 @@ impl EnvBackend for EnvInstance { Err(ExtError::KeyNotFound) => return Ok(None), Err(_) => panic!("encountered unexpected error"), } - let decoded = scale::Decode::decode(&mut &output[..])?; + let decoded = Storable::decode(&mut &output[..])?; Ok(Some(decoded)) } @@ -280,7 +280,7 @@ impl EnvBackend for EnvInstance { where R: scale::Encode, { - let mut scope = EncodeScope::from(&mut self.buffer[..]); + let mut scope = super::EncodeScope::from(&mut self.buffer[..]); return_value.encode_to(&mut scope); let len = scope.len(); ext::return_value(flags, &self.buffer[..][..len]); diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 50a4c955d29..4ab80cb40bd 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -799,8 +799,8 @@ impl Dispatch<'_> { fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) { if mutates { ::ink_storage::traits::push_storage::<#storage_ident>( + &<#storage_ident as ::ink_storage::traits::StorageKey>::KEY, &contract, - &<#storage_ident as ::ink_storage::traits::KeyHolder>::KEY, ); } } @@ -814,7 +814,7 @@ impl Dispatch<'_> { ::core::mem::ManuallyDrop::new( ::ink_storage::pull_or_init!( #storage_ident, - <#storage_ident as ::ink_storage::traits::KeyHolder>::KEY + <#storage_ident as ::ink_storage::traits::StorageKey>::KEY ) ); diff --git a/crates/lang/codegen/src/generator/metadata.rs b/crates/lang/codegen/src/generator/metadata.rs index def5eaf5bdc..7016685cf51 100644 --- a/crates/lang/codegen/src/generator/metadata.rs +++ b/crates/lang/codegen/src/generator/metadata.rs @@ -63,7 +63,7 @@ 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 key = quote! { <#storage_ident as ::ink_storage::traits::StorageKey>::KEY }; let layout_key = quote! { <::ink_metadata::layout::LayoutKey diff --git a/crates/lang/codegen/src/generator/storage_item.rs b/crates/lang/codegen/src/generator/storage_item.rs index 1d4d8cffc7f..fe778ad7e60 100644 --- a/crates/lang/codegen/src/generator/storage_item.rs +++ b/crates/lang/codegen/src/generator/storage_item.rs @@ -62,8 +62,8 @@ impl GenerateCode for StorageItem<'_> { ))] #[derive( ::ink_storage::traits::Item, - ::ink_storage::traits::KeyHolder, - ::ink_storage::traits::Storable, + ::ink_storage::traits::StorageKey, + ::ink_primitives::traits::Storable, )] }; } @@ -233,7 +233,8 @@ fn convert_into_storage_field( struct_ident.to_string().as_str(), variant_name.as_str(), field_name.as_str(), - ); + ) + .unwrap(); let mut new_field = field.clone(); let ty = field.ty.clone().to_token_stream(); diff --git a/crates/lang/ir/src/ir/storage_item/mod.rs b/crates/lang/ir/src/ir/storage_item/mod.rs index 3d3304941dd..2ed1ce85b8a 100644 --- a/crates/lang/ir/src/ir/storage_item/mod.rs +++ b/crates/lang/ir/src/ir/storage_item/mod.rs @@ -78,7 +78,7 @@ impl StorageItem { &self.ast.data } - /// Returns salt for storage key + /// Returns salt for storage key. pub fn salt(&self) -> TokenStream2 { if let Some(param) = self.ast.find_salt() { param.ident.to_token_stream() diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index c004c05ee1e..896762fe469 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -697,10 +697,10 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// Mapping, /// }; /// use ink_storage::traits::{ -/// KeyHolder, +/// StorageKey, /// Item, -/// Storable, /// }; +/// use ink_primitives::traits::Storable; /// /// #[derive(scale::Decode, scale::Encode)] /// struct Packed { @@ -734,7 +734,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// } /// /// #[ink_lang::storage_item(derive = false)] -/// #[derive(Storable, Item, KeyHolder)] +/// #[derive(Storable, Item, StorageKey)] /// #[cfg_attr( /// feature = "std", /// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) @@ -763,7 +763,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// } /// /// #[ink_lang::storage_item] -/// struct NonPackedComplex { +/// struct NonPackedComplex { /// s1: (String, u128, Packed), /// s2: Mapping, /// s3: Lazy, @@ -791,11 +791,12 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// use ink_storage::Mapping; /// use ink_storage::traits::{ /// Item, -/// KeyHolder, -/// Storable, +/// StorageKey, /// }; +/// use ink_primitives::traits::Storable; +/// /// #[ink_lang::storage_item(derive = false)] -/// #[derive(Item, Storable, KeyHolder)] +/// #[derive(Item, Storable, StorageKey)] /// struct NonPackedGeneric { /// s1: u32, /// s2: Mapping, diff --git a/crates/lang/src/codegen/dispatch/execution.rs b/crates/lang/src/codegen/dispatch/execution.rs index 66963fc46f7..39814bda425 100644 --- a/crates/lang/src/codegen/dispatch/execution.rs +++ b/crates/lang/src/codegen/dispatch/execution.rs @@ -24,10 +24,10 @@ use ink_env::{ Environment, ReturnFlags, }; +use ink_primitives::traits::Storable; use ink_storage::traits::{ push_storage, - KeyHolder, - Storable, + StorageKey, }; use scale::Encode; @@ -57,7 +57,7 @@ where #[inline] pub fn execute_constructor(f: F) -> Result<(), DispatchError> where - Contract: Storable + KeyHolder + ContractEnv, + Contract: Storable + StorageKey + ContractEnv, F: FnOnce() -> R, as ConstructorReturnType>::ReturnValue: Encode, private::Seal: ConstructorReturnType, @@ -68,7 +68,7 @@ where // Constructor is infallible or is fallible but succeeded. // // This requires us to sync back the changes of the contract storage. - push_storage::(contract, &Contract::KEY); + push_storage::(&Contract::KEY, contract); Ok(()) } Err(_) => { 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 index 81de75a1d0e..47d5871c4c9 100644 --- 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 @@ -1,6 +1,6 @@ #[ink_lang::storage_item(derive = "false")] #[derive(Default)] -struct Contract> { +struct Contract> { a: u16, b: u64, c: u128, 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 index 5322851d07b..5c0346662c9 100644 --- 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 @@ -1,6 +1,6 @@ #[ink_lang::storage_item(derive)] #[derive(Default)] -struct Contract> { +struct Contract> { a: u16, b: u64, c: u128, 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 index cdef4fce001..47d9542a648 100644 --- 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 @@ -39,11 +39,11 @@ note: required because it appears within the type `Contract` 10 | struct Contract { | ^^^^^^^^ note: required by a bound in `Storable` - --> $WORKSPACE/crates/storage/src/traits/storage.rs + --> $WORKSPACE/crates/primitives/src/traits.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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -64,11 +64,11 @@ note: required because it appears within the type `Contract` 10 | struct Contract { | ^^^^^^^^ note: required by a bound in `Storable` - --> $WORKSPACE/crates/storage/src/traits/storage.rs + --> $WORKSPACE/crates/primitives/src/traits.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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -90,7 +90,7 @@ note: required by a bound in `Result` | | 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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -115,4 +115,4 @@ note: required by a bound in `Result` | | 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) + = note: this error originates in the derive macro `::ink_primitives::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.stderr b/crates/lang/tests/ui/storage_item/fail/collections_only_packed_2.stderr index ce6507c03ff..d9c1faca002 100644 --- 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 @@ -36,11 +36,11 @@ note: required because it appears within the type `Contract` 10 | struct Contract { | ^^^^^^^^ note: required by a bound in `Storable` - --> $WORKSPACE/crates/storage/src/traits/storage.rs + --> $WORKSPACE/crates/primitives/src/traits.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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -58,11 +58,11 @@ note: required because it appears within the type `Contract` 10 | struct Contract { | ^^^^^^^^ note: required by a bound in `Storable` - --> $WORKSPACE/crates/storage/src/traits/storage.rs + --> $WORKSPACE/crates/primitives/src/traits.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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -84,7 +84,7 @@ note: required by a bound in `Result` | | 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) + = note: this error originates in the derive macro `::ink_primitives::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 @@ -106,4 +106,4 @@ note: required by a bound in `Result` | | 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) + = note: this error originates in the derive macro `::ink_primitives::traits::Storable` (in Nightly builds, run with -Z macro-backtrace for more info) 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 index 10622990993..2c7aaa4b614 100644 --- a/crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs +++ b/crates/lang/tests/ui/storage_item/pass/argument_derive_false.rs @@ -1,19 +1,19 @@ +use ink_primitives::traits::Storable; use ink_storage::traits::{ - KeyHolder, ManualKey, - Storable, + StorageKey, }; #[ink_lang::storage_item(derive = false)] #[derive(Default)] -struct Contract> { +struct Contract> { a: u16, b: u64, c: u128, } // Disabling of deriving allow to implement the trait manually -impl Storable for Contract { +impl Storable for Contract { fn encode(&self, _dest: &mut T) {} fn decode(_input: &mut I) -> Result { 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 index 1c6ea5b81b2..e7b634a891f 100644 --- 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 @@ -3,7 +3,7 @@ use ink_primitives::KeyComposer; use ink_storage::{ traits::{ AutoKey, - KeyHolder, + StorageKey, }, Lazy, Mapping, @@ -32,7 +32,7 @@ enum Packed { #[ink_lang::storage_item] #[derive(Default)] -enum NonPacked { +enum NonPacked { #[default] None, A(Mapping), @@ -64,7 +64,7 @@ fn main() { ); contract.a.set(&NonPacked::<_>::A(Default::default())); - let variant = if let NonPacked::<_>::A(variant) = contract.a.get() { + let variant = if let Some(NonPacked::<_>::A(variant)) = contract.a.get() { variant } else { panic!("Wrong variant") @@ -78,7 +78,7 @@ fn main() { ); contract.a.set(&NonPacked::<_>::B(Default::default())); - let variant = if let NonPacked::<_>::B(variant) = contract.a.get() { + let variant = if let Some(NonPacked::<_>::B(variant)) = contract.a.get() { variant } else { panic!("Wrong variant") @@ -92,7 +92,7 @@ fn main() { ); contract.a.set(&NonPacked::<_>::C(Default::default())); - let variant = if let NonPacked::<_>::C(variant) = contract.a.get() { + let variant = if let Some(NonPacked::<_>::C(variant)) = contract.a.get() { variant } else { panic!("Wrong variant") @@ -106,7 +106,7 @@ fn main() { ); contract.a.set(&NonPacked::<_>::D(Default::default())); - let variant = if let NonPacked::<_>::D(variant) = contract.a.get() { + let variant = if let Some(NonPacked::<_>::D(variant)) = contract.a.get() { variant } else { panic!("Wrong variant") @@ -120,7 +120,7 @@ fn main() { ); contract.a.set(&NonPacked::<_>::E(Default::default())); - let variant = if let NonPacked::<_>::E(variant) = contract.a.get() { + let variant = if let Some(NonPacked::<_>::E(variant)) = contract.a.get() { variant } else { panic!("Wrong variant") @@ -136,7 +136,7 @@ fn main() { contract.a.set(&NonPacked::<_>::F { a: Default::default(), }); - let variant = if let NonPacked::<_>::F { a } = contract.a.get() { + let variant = if let Some(NonPacked::<_>::F { a }) = contract.a.get() { a } else { panic!("Wrong variant") 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 index 5ff3fac8523..bc5fee55a8a 100644 --- 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 @@ -3,7 +3,7 @@ use ink_primitives::KeyComposer; use ink_storage::{ traits::{ AutoKey, - KeyHolder, + StorageKey, }, Lazy, Mapping, @@ -25,7 +25,7 @@ struct Packed { #[ink_lang::storage_item] #[derive(Default)] -struct NonPacked { +struct NonPacked { a: Mapping, b: Lazy, c: Lazy, 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 index 9ddd1f8e17b..a65bfeb7374 100644 --- a/crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs +++ b/crates/lang/tests/ui/storage_item/pass/complex_packed_enum.rs @@ -5,7 +5,7 @@ use ink_prelude::{ }, vec::Vec, }; -use ink_storage::traits::Storable; +use ink_primitives::traits::Storable; #[derive(Default, PartialEq, Eq, PartialOrd, Ord, scale::Encode, scale::Decode)] #[cfg_attr( 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 index fbaba78312e..f868ab3ae1b 100644 --- a/crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs +++ b/crates/lang/tests/ui/storage_item/pass/complex_packed_struct.rs @@ -5,7 +5,7 @@ use ink_prelude::{ }, vec::Vec, }; -use ink_storage::traits::Storable; +use ink_primitives::traits::Storable; #[derive(Default, PartialEq, Eq, PartialOrd, Ord, scale::Encode, scale::Decode)] #[cfg_attr( 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 index b518454e3de..84de1b7d914 100644 --- 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 @@ -1,15 +1,15 @@ use ink_storage::traits::{ - KeyHolder, ManualKey, + StorageKey, }; #[ink_lang::storage_item] -struct Contract> { +struct Contract> { a: u16, b: u16, c: u16, } fn main() { - assert_eq!(::KEY, 123); + 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 index f49120bef50..5a1e689c4b4 100644 --- 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 @@ -4,12 +4,12 @@ use ink_storage::{ }; #[ink_lang::storage_item] -struct Contract> { +struct Contract> { a: u16, b: u16, c: u16, } fn main() { - assert_eq!(::KEY, 123); + 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 index 3ce45400535..5b28386e0c9 100644 --- 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 @@ -1,12 +1,12 @@ use ink_storage::traits::ManualKey; #[ink_lang::storage_item] -struct Contract> { +struct Contract> { a: u16, b: u16, c: u16, } fn main() { - assert_eq!(::KEY, 123); + 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 index 1b3bb886386..ebdfb9b6641 100644 --- 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 @@ -1,12 +1,12 @@ use ink_storage::traits::ManualKey; #[ink_lang::storage_item] -struct Contract> { +struct Contract> { a: u16, b: u16, c: u16, } fn main() { - assert_eq!(::KEY, 123); + 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 index f9d1bd761dd..8211be89b43 100644 --- 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 @@ -1,6 +1,6 @@ use ink_primitives::KeyComposer; use ink_storage::{ - traits::KeyHolder, + traits::StorageKey, Lazy, Mapping, }; 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 index 7d265a835fd..eccac53a487 100644 --- a/crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs +++ b/crates/lang/tests/ui/storage_item/pass/packed_tuple_struct.rs @@ -1,4 +1,4 @@ -use ink_storage::traits::Storable; +use ink_primitives::traits::Storable; #[derive(Default, scale::Encode, scale::Decode)] #[cfg_attr( diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index f40d65a56e6..1340714f298 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -20,6 +20,8 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } sha2-const = { version = "0.1.2", default-features = false } +ink_primitives_derive = { version = "4.0.0", path = "derive", default-features = false } + [features] default = ["std"] std = [ diff --git a/crates/primitives/derive/Cargo.toml b/crates/primitives/derive/Cargo.toml new file mode 100644 index 00000000000..fa3c7257711 --- /dev/null +++ b/crates/primitives/derive/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "ink_primitives_derive" +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_primitives_derive" +homepage = "https://www.parity.io/" +description = "[ink!] Derive macros for common ink_primitives defined traits." +keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"] +categories = ["no-std", "embedded"] +include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +syn = { version = "1", features = ["full"] } +proc-macro2 = "1" +synstructure = "0.12.4" + +[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 = ".." } +ink_metadata = { version = "4.0.0", path = "../../metadata" } +ink_prelude = { version = "4.0.0", path = "../../prelude/" } diff --git a/crates/primitives/derive/LICENSE b/crates/primitives/derive/LICENSE new file mode 100644 index 00000000000..6b0b1270ff0 --- /dev/null +++ b/crates/primitives/derive/LICENSE @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/crates/primitives/derive/README.md b/crates/primitives/derive/README.md new file mode 100644 index 00000000000..5e0e91bbc8b --- /dev/null +++ b/crates/primitives/derive/README.md @@ -0,0 +1,289 @@ +

+ ink! +

+ Parity's ink! for writing smart contracts +

+ +[![linux][a1]][a2] [![codecov][c1]][c2] [![coveralls][d1]][d2] [![loc][e1]][e2] [![stack-exchange][s1]][s2] + +[a1]: https://gitlab.parity.io/parity/ink/badges/master/pipeline.svg +[a2]: https://gitlab.parity.io/parity/ink/pipelines?ref=master +[c1]: https://codecov.io/gh/paritytech/ink/branch/master/graph/badge.svg +[c2]: https://codecov.io/gh/paritytech/ink/branch/master +[d1]: https://coveralls.io/repos/github/paritytech/ink/badge.svg?branch=master +[d2]: https://coveralls.io/github/paritytech/ink?branch=master +[e1]: https://tokei.rs/b1/github/paritytech/ink?category=code +[e2]: https://github.com/Aaronepower/tokei#badges +[f1]: https://img.shields.io/badge/click-blue.svg +[f2]: https://paritytech.github.io/ink/ink_storage +[g1]: https://img.shields.io/badge/click-blue.svg +[g2]: https://paritytech.github.io/ink/ink_env +[i1]: https://img.shields.io/badge/click-blue.svg +[i2]: https://paritytech.github.io/ink/ink_prelude +[j1]: https://img.shields.io/badge/click-blue.svg +[j2]: https://paritytech.github.io/ink/ink_lang +[k1]: https://img.shields.io/badge/matrix-chat-brightgreen.svg?style=flat +[k2]: https://riot.im/app/#/room/#ink:matrix.parity.io +[l1]: https://img.shields.io/discord/722223075629727774?style=flat-square&label=discord +[l2]: https://discord.com/invite/wGUDt2p +[s1]: https://img.shields.io/badge/click-white.svg?logo=StackExchange&label=ink!%20Support%20on%20StackExchange&labelColor=white&color=blue +[s2]: https://substrate.stackexchange.com/questions/tagged/ink?tab=Votes + +> squink, the ink! mascotink! is an [eDSL](https://wiki.haskell.org/Embedded_domain_specific_language) to write smart contracts in Rust for blockchains built on the [Substrate](https://github.com/paritytech/substrate) framework. ink! contracts are compiled to WebAssembly. + +
+ +[Guided Tutorial for Beginners](https://docs.substrate.io/tutorials/v3/ink-workshop/pt1)  •   +[ink! Documentation Portal](https://ink.substrate.io)  •   +[Developer Documentation](https://paritytech.github.io/ink/ink_lang/) + + +
+
+ +More relevant links: +* [Substrate Stack Exchange](https://substrate.stackexchange.com/questions/tagged/ink?tab=Votes) ‒ Forum for getting your ink! questions answered +* [`cargo-contract`](https://github.com/paritytech/cargo-contract) ‒ CLI tool for ink! contracts +* [Contracts UI](https://contracts-ui.substrate.io/) ‒ Frontend for contract instantiation and interaction +* [Substrate Contracts Node](https://github.com/paritytech/substrate-contracts-node) ‒ Simple Substrate blockchain which includes smart contract functionality +* We post announcements on [Matrix][k2] and [Discord][l2] (in the + [`ink_smart-contracts`](https://discord.com/channels/722223075629727774/765280480609828864) channel). + + +## Table of Contents + +* [Play with It](#play-with-it) +* [Usage](#usage) +* [Hello, World! ‒ The Flipper](#hello-world--the-flipper) +* [Examples](#examples) +* [How it Works](#how-it-works) +* [ink! Macros & Attributes Overview](#ink-macros--attributes-overview) + * [Entry Point](#entry-point) + * [Trait Definitions](#trait-definitions) + * [Off-chain Testing](#off-chain-testing) +* [Developer Documentation](#developer-documentation) +* [Community Badges](#community-badges) +* [Contributing](#contributing) +* [License](#license) + + +## Play with It + +If you want to have a local setup you can use our [`substrate-contracts-node`](https://github.com/paritytech/substrate-contracts-node) for a quickstart. +It's a simple Substrate blockchain which includes the Substrate module for smart contract functionality ‒ the `contracts` pallet (see [How it Works](#how-it-works) for more). + +We also have a live testnet on [Rococo](https://github.com/paritytech/cumulus/#rococo-) +called [Canvas](https://ink.substrate.io/canvas). Canvas is a Substrate based +parachain which supports ink! smart contracts. For further instructions on using this +testnet, follow the instructions in the +[our documentation](https://ink.substrate.io/canvas#rococo-deployment). + +For both types of chains the [Contracts UI](https://contracts-ui.substrate.io/) +can be used to instantiate your contract to a chain and interact with it. + +## Usage + +A prerequisite for compiling smart contracts is to have Rust and Cargo installed. Here's [an installation guide](https://doc.rust-lang.org/cargo/getting-started/installation.html). + +We recommend installing [`cargo-contract`](https://github.com/paritytech/cargo-contract) as well. +It's a CLI tool which helps set up and manage WebAssembly smart contracts written with ink!: + +``` +cargo install cargo-contract --force +``` + +Use the `--force` to ensure you are updated to the most recent `cargo-contract` version. + +In order to initialize a new ink! project you can use: + +``` +cargo contract new flipper +``` + +This will create a folder `flipper` in your work directory. +The folder contains a scaffold `Cargo.toml` and a `lib.rs`, which both contain the necessary building blocks for using ink!. + +The `lib.rs` contains our hello world contract ‒ the `Flipper`, which we explain in the next section. + +In order to build the contract just execute this command in the `flipper` folder: +``` +cargo +nightly contract build +``` + +As a result you'll get a file `target/flipper.wasm` file, a `metadata.json` file and a `.contract` file in the `target` folder of your contract. +The `.contract` file combines the Wasm and metadata into one file and needs to be used when instantiating the contract. + + +## Hello, World! ‒ The Flipper + +The `Flipper` contract is a simple contract containing only a single `bool` value. +It provides methods to +* flip its value from `true` to `false` (and vice versa) and +* return the current state. + + +Below you can see the code using the `ink_lang` version of ink!. + +```rust +use ink_lang as ink; + +#[ink::contract] +mod flipper { + /// The storage of the flipper contract. + #[ink(storage)] + pub struct Flipper { + /// The single `bool` value. + value: bool, + } + + impl Flipper { + /// Instantiates a new Flipper contract and initializes + /// `value` to `init_value`. + #[ink(constructor)] + pub fn new(init_value: bool) -> Self { + Self { + value: init_value, + } + } + + /// Flips `value` from `true` to `false` or vice versa. + #[ink(message)] + pub fn flip(&mut self) { + self.value = !self.value; + } + + /// Returns the current state of `value`. + #[ink(message)] + pub fn get(&self) -> bool { + self.value + } + } + + /// Simply execute `cargo test` in order to test your contract + /// using the below unit tests. + #[cfg(test)] + mod tests { + use super::*; + use ink_lang as ink; + + #[ink::test] + fn it_works() { + let mut flipper = Flipper::new(false); + assert_eq!(flipper.get(), false); + flipper.flip(); + assert_eq!(flipper.get(), true); + } + } +} +``` + +The [`flipper/src/lib.rs`](https://github.com/paritytech/ink/blob/master/examples/flipper/lib.rs) +file in our examples folder contains exactly this code. Run `cargo contract build` to build your +first ink! smart contract. + +## Examples + +In the `examples` folder you'll find a number of examples written in ink!. + +Some of the most interesting ones: + +* `delegator` ‒ Implements cross-contract calling. +* `trait-erc20` ‒ Defines a trait for `Erc20` contracts and implements it. +* `erc721` ‒ An exemplary implementation of `Erc721` NFT tokens. +* `dns` ‒ A simple `DomainNameService` smart contract. +* …and more, just rummage through the folder 🙃. + +To build a single example navigate to the root of the example and run: +``` +cargo contract build +``` + +You should now have an `.contract` file in the `target` folder of the contract. + +For information on how to upload this file to a chain, please have a look at the [Play with It](#play-with-it) section or our [smart contracts workshop](https://docs.substrate.io/tutorials/v3/ink-workshop/pt1). + + +## How it Works + +* Substrate's [Framework for Runtime Aggregation of Modularized Entities (FRAME)](https://docs.substrate.io/v3/runtime/frame) +contains a module which implements an API for typical functions smart contracts need (storage,querying information about accounts, …). +This module is called the `contracts` pallet, +* The `contracts` pallet requires smart contracts to be uploaded to the blockchain as a Wasm blob. +* ink! is a smart contract language which targets the API exposed by `contracts`. +Hence ink! contracts are compiled to Wasm. +* When executing `cargo contract build` an additional file `metadata.json` is created. +It contains information about e.g. what methods the contract provides for others to call. + +## ink! Macros & Attributes Overview + +### Entry Point + +In a module annotated with `#[ink::contract]` these attributes are available: + +| Attribute | Where Applicable | Description | +|:--|:--|:--| +| `#[ink(storage)]` | On `struct` definitions. | Defines the ink! storage struct. There can only be one ink! storage definition per contract. | +| `#[ink(message)]` | Applicable to methods. | Flags a method for the ink! storage struct as message making it available to the API for calling the contract. | +| `#[ink(constructor)]` | Applicable to method. | Flags a method for the ink! storage struct as constructor making it available to the API for instantiating the contract. | +| `#[ink(event)]` | On `struct` definitions. | Defines an ink! event. A contract can define multiple such ink! events. | +| `#[ink(anonymous)]` | Applicable to ink! events. | Tells the ink! codegen to treat the ink! event as anonymous which omits the event signature as topic upon emitting. Very similar to anonymous events in Solidity. | +| `#[ink(topic)]` | Applicable on ink! event field. | Tells the ink! codegen to provide a topic hash for the given field. Every ink! event can only have a limited number of such topic field. Similar semantics as to indexed event arguments in Solidity. | +| `#[ink(payable)]` | Applicable to ink! messages. | Allows receiving value as part of the call of the ink! message. ink! constructors are implicitly payable. | +| `#[ink(selector = S:u32)]` | Applicable to ink! messages and ink! constructors. | Specifies a concrete dispatch selector for the flagged entity. This allows a contract author to precisely control the selectors of their APIs making it possible to rename their API without breakage. | +| `#[ink(selector = _)]` | Applicable to ink! messages. | Specifies a fallback message that is invoked if no other ink! message matches a selector. | +| `#[ink(namespace = N:string)]` | Applicable to ink! trait implementation blocks. | Changes the resulting selectors of all the ink! messages and ink! constructors within the trait implementation. Allows to disambiguate between trait implementations with overlapping message or constructor names. Use only with great care and consideration! | +| `#[ink(impl)]` | Applicable to ink! implementation blocks. | Tells the ink! codegen that some implementation block shall be granted access to ink! internals even without it containing any ink! messages or ink! constructors. | + +See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a more detailed description of those and also for details on the `#[ink::contract]` macro. + +### Trait Definitions + +Use `#[ink::trait_definition]` to define your very own trait definitions that are then implementable by ink! smart contracts. +See e.g. the [`examples/trait-erc20`](https://github.com/paritytech/ink/blob/v3.0.0-rc5/examples/trait-erc20/lib.rs#L35-L37) contract on how to utilize it or [the documentation](https://paritytech.github.io/ink/ink_lang/attr.trait_definition.html) for details. + +### Off-chain Testing + +The `#[ink::test]` procedural macro enables off-chain testing. See e.g. the [`examples/erc20`](https://github.com/paritytech/ink/blob/v3.0.0-rc5/examples/erc20/lib.rs#L248-L250) contract on how to utilize those or [the documentation](https://paritytech.github.io/ink/ink_lang/attr.test.html) for details. + +## Developer Documentation + +We have [a very comprehensive documentation portal](https://ink.substrate.io), +but if you are looking for the crate level documentation itself, then these are +the relevant links: + +| Crate | Docs | Description | +|:--|:--|:--| +`ink_lang` | [![][j1]][j2] | Language features exposed by ink!. See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a detailed description of attributes which you can use in an `#[ink::contract]`. | +`ink_storage` | [![][f1]][f2] | Data structures available in ink!. | +`ink_env` | [![][g1]][g2] | Low-level interface for interacting with the smart contract Wasm executor. Contains [the off-chain testing API](https://paritytech.github.io/ink/ink_env/test/index.html) as well. | +`ink_prelude` | [![][i1]][i2] | Common API for no_std and std to access alloc crate types. | + +## Community Badges + +### Normal Design + +[![Built with ink!](.images/badge.svg)](https://github.com/paritytech/ink) + +```markdown +[![Built with ink!](https://raw.githubusercontent.com/paritytech/ink/master/.images/badge.svg)](https://github.com/paritytech/ink) +``` + +### Flat Design + +[![Built with ink!](.images/badge_flat.svg)](https://github.com/paritytech/ink) + +```markdown +[![Built with ink!](https://raw.githubusercontent.com/paritytech/ink/master/.images/badge_flat.svg)](https://github.com/paritytech/ink) +``` + +## Contributing + +Visit our [contribution guidelines](CONTRIBUTING.md) for more information. + +Use the scripts provided under `scripts/check-*` directory in order to run checks on either the workspace or all examples. Please do this before pushing work in a PR. + +## License + +The entire code within this repository is licensed under the [Apache License 2.0](LICENSE). + +Please [contact us](https://www.parity.io/contact/) if you have questions about the licensing of our products. diff --git a/crates/primitives/derive/src/lib.rs b/crates/primitives/derive/src/lib.rs new file mode 100644 index 00000000000..57ad3981d6c --- /dev/null +++ b/crates/primitives/derive/src/lib.rs @@ -0,0 +1,46 @@ +// 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. + +//! 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. + +extern crate proc_macro; + +mod storable; + +#[cfg(test)] +mod tests; + +use self::storable::storable_derive; +synstructure::decl_derive!( + [Storable] => + /// Derives `ink_storage`'s `Storable` trait for the given `struct`, `enum` or `union`. + /// + /// # Examples + /// + /// ``` + /// use ink_primitives::traits::Storable; + /// + /// #[derive(Storable)] + /// struct NamedFields { + /// a: u32, + /// b: [u32; 1], + /// } + /// + /// let value = ::decode(&mut &[123, 123][..]); + /// ``` + storable_derive +); diff --git a/crates/storage/derive/src/storable.rs b/crates/primitives/derive/src/storable.rs similarity index 87% rename from crates/storage/derive/src/storable.rs rename to crates/primitives/derive/src/storable.rs index 0661ee1853b..dc26621e40a 100644 --- a/crates/storage/derive/src/storable.rs +++ b/crates/primitives/derive/src/storable.rs @@ -27,18 +27,18 @@ fn storable_struct_derive(s: &synstructure::Structure) -> TokenStream2 { let ty = &field.ty; let span = ty.span(); quote_spanned!(span => - <#ty as ::ink_storage::traits::Storable>::decode(__input)? + <#ty as ::ink_primitives::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); + ::ink_primitives::traits::Storable::encode(#binding, __dest); ) }); s.gen_impl(quote! { - gen impl ::ink_storage::traits::Storable for @Self { + gen impl ::ink_primitives::traits::Storable for @Self { #[inline(always)] #[allow(non_camel_case_types)] fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { @@ -68,7 +68,7 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { let ty = &field.ty; let span = ty.span(); quote_spanned!(span => - <#ty as ::ink_storage::traits::Storable>::decode(__input)? + <#ty as ::ink_primitives::traits::Storable>::decode(__input)? ) }) }) @@ -87,12 +87,12 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { let fields = variant.bindings().iter().map(|field| { let span = field.ast().ty.span(); quote_spanned!(span => - ::ink_storage::traits::Storable::encode(#field, __dest); + ::ink_primitives::traits::Storable::encode(#field, __dest); ) }); quote! { #pat => { - { <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&#index, __dest); } + { <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode(&#index, __dest); } #( { #fields } )* @@ -100,12 +100,12 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { } }); s.gen_impl(quote! { - gen impl ::ink_storage::traits::Storable for @Self { + gen impl ::ink_primitives::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)? { + match <::core::primitive::u8 as ::ink_primitives::traits::Storable>::decode(__input)? { #decode_body _ => unreachable!("encountered invalid enum discriminant"), } diff --git a/crates/primitives/derive/src/tests/mod.rs b/crates/primitives/derive/src/tests/mod.rs new file mode 100644 index 00000000000..a8562ff642f --- /dev/null +++ b/crates/primitives/derive/src/tests/mod.rs @@ -0,0 +1,47 @@ +// 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 storable; + +#[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/storable.rs b/crates/primitives/derive/src/tests/storable.rs similarity index 79% rename from crates/storage/derive/src/tests/storable.rs rename to crates/primitives/derive/src/tests/storable.rs index ef812de5c0d..175b99fc522 100644 --- a/crates/storage/derive/src/tests/storable.rs +++ b/crates/primitives/derive/src/tests/storable.rs @@ -30,7 +30,7 @@ fn unit_struct_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for UnitStruct { + impl ::ink_primitives::traits::Storable for UnitStruct { #[inline(always)] #[allow(non_camel_case_types)] fn decode<__ink_I: ::scale::Input>(__input: &mut __ink_I) -> ::core::result::Result { @@ -62,15 +62,15 @@ fn struct_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for NamedFields { + impl ::ink_primitives::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)?, + a : ::decode(__input)?, + b : <[u8; 32] as ::ink_primitives::traits::Storable>::decode(__input)?, + d : as ::ink_primitives::traits::Storable>::decode(__input)?, } ) } @@ -86,19 +86,19 @@ fn struct_works() { d: __binding_2, } => { { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_2, __dest ); @@ -122,12 +122,12 @@ fn one_variant_enum_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for OneVariantEnum { + impl ::ink_primitives::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)? + match <::core::primitive::u8 as ::ink_primitives::traits::Storable>::decode(__input)? { 0u8 => OneVariantEnum::A, _ => unreachable!("encountered invalid enum discriminant"), @@ -141,7 +141,7 @@ fn one_variant_enum_works() { match self { OneVariantEnum::A => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode( &0u8, __dest ); @@ -167,21 +167,21 @@ fn enum_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for MixedEnum { + impl ::ink_primitives::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)? + match <::core::primitive::u8 as ::ink_primitives::traits::Storable>::decode(__input)? { 0u8 => MixedEnum::A, 1u8 => MixedEnum::B( - ::decode(__input)?, - <[u8; 32] as ::ink_storage::traits::Storable>::decode(__input)?, + ::decode(__input)?, + <[u8; 32] as ::ink_primitives::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)?, + a: < i32 as ::ink_primitives::traits::Storable>::decode(__input)?, + b: <(bool, i32) as ::ink_primitives::traits::Storable>::decode(__input)?, }, _ => unreachable!("encountered invalid enum discriminant"), } @@ -194,7 +194,7 @@ fn enum_works() { match self { MixedEnum::A => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode( &0u8, __dest ); @@ -202,19 +202,19 @@ fn enum_works() { } MixedEnum::B(__binding_0, __binding_1,) => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode( &1u8, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); @@ -225,18 +225,18 @@ fn enum_works() { b: __binding_1, } => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode( + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode( &2u8, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); @@ -256,8 +256,8 @@ fn generic_struct_works() { storable_derive { struct GenericStruct where - T1: ::ink_storage::traits::Packed, - T2: ::ink_storage::traits::Packed, + T1: ::scale::Decode, + T2: ::scale::Encode, { a: T1, b: (T1, T2), @@ -265,22 +265,22 @@ fn generic_struct_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for GenericStruct + impl ::ink_primitives::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 + T1: ::scale::Decode, + T2: ::scale::Encode, + T1: ::ink_primitives::traits::Storable, + (T1 , T2): ::ink_primitives::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( + a: ::decode( __input )?, - b: <(T1, T2) as ::ink_storage::traits::Storable>::decode( + b: <(T1, T2) as ::ink_primitives::traits::Storable>::decode( __input )?, } @@ -296,13 +296,13 @@ fn generic_struct_works() { b: __binding_1, } => { { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); @@ -327,24 +327,24 @@ fn generic_enum_works() { } expands to { const _: () = { - impl ::ink_storage::traits::Storable for GenericEnum + impl ::ink_primitives::traits::Storable for GenericEnum where - T1: ::ink_storage::traits::Storable, - T2: ::ink_storage::traits::Storable + T1: ::ink_primitives::traits::Storable, + T2: ::ink_primitives::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)? + match <::core::primitive::u8 as ::ink_primitives::traits::Storable>::decode(__input)? { 0u8 => GenericEnum::Tuple( - ::decode(__input)?, - ::decode(__input)?, + ::decode(__input)?, + ::decode(__input)?, ), 1u8 => GenericEnum::Named { - a: ::decode(__input)?, - b: ::decode(__input)?, + a: ::decode(__input)?, + b: ::decode(__input)?, }, _ => unreachable!("encountered invalid enum discriminant"), } @@ -357,16 +357,16 @@ fn generic_enum_works() { match self { GenericEnum::Tuple(__binding_0, __binding_1,) => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&0u8, __dest); + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode(&0u8, __dest); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); @@ -377,16 +377,16 @@ fn generic_enum_works() { b: __binding_1, } => { { - <::core::primitive::u8 as ::ink_storage::traits::Storable>::encode(&1u8, __dest); + <::core::primitive::u8 as ::ink_primitives::traits::Storable>::encode(&1u8, __dest); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_0, __dest ); } { - ::ink_storage::traits::Storable::encode( + ::ink_primitives::traits::Storable::encode( __binding_1, __dest ); diff --git a/crates/primitives/src/key.rs b/crates/primitives/src/key.rs index a4e84843d28..b2d8b96ac27 100644 --- a/crates/primitives/src/key.rs +++ b/crates/primitives/src/key.rs @@ -15,15 +15,25 @@ use ink_prelude::vec; use sha2_const::Sha256; +/// 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. 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. + /// Concatenate two `Key` into one during compilation. pub const fn concat(left: Key, right: Key) -> Key { + // If one of the keys is zero, then return another without hashing. + // If both keys are non-zero, return the hash of both keys. match (left, right) { (0, 0) => 0, (0, _) => right, @@ -45,24 +55,39 @@ impl KeyComposer { /// Returns the storage key from the supplied `bytes`. pub const fn from_bytes(bytes: &[u8]) -> Key { + if bytes.is_empty() { + return 0 + } + let hash = Sha256::new().update(bytes).finalize(); Key::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]) } - /// # Note - /// - /// - `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. If `variant_name` is not empty 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 { + /// + /// # Note + /// + /// - `variant_name` is empty for structures and unions. + /// - if the field is unnamed then `field_name` is `"{}"` where `{}` is a number of the field. + pub fn compute_key( + struct_name: &str, + variant_name: &str, + field_name: &str, + ) -> Result { + if struct_name.is_empty() { + return Err(Error::StructNameIsEmpty) + } + if field_name.is_empty() { + return Err(Error::FieldNameIsEmpty) + } + let separator = &b"::"[..]; let composed_key = if !variant_name.is_empty() { vec![ @@ -75,6 +100,59 @@ impl KeyComposer { vec![struct_name.as_bytes(), field_name.as_bytes()].join(separator) }; - Self::from_bytes(composed_key.as_slice()) + Ok(Self::from_bytes(composed_key.as_slice())) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Error { + StructNameIsEmpty, + FieldNameIsEmpty, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn concat_works_correct() { + assert_eq!(KeyComposer::concat(0, 13), 13); + assert_eq!(KeyComposer::concat(31, 0), 31); + assert_eq!(KeyComposer::concat(31, 13), 0xD83A5CD8); + assert_eq!(KeyComposer::concat(0, 0), 0); + } + + #[test] + fn from_str_works_correct() { + assert_eq!(KeyComposer::from_str(""), 0); + assert_eq!(KeyComposer::from_str("123"), 0xa665a459); + assert_eq!(KeyComposer::from_str("Hello world"), 0x64ec88ca); + } + + #[test] + fn from_bytes_works_correct() { + assert_eq!(KeyComposer::from_bytes(b""), 0); + assert_eq!(KeyComposer::from_bytes(b"123"), 0xa665a459); + assert_eq!(KeyComposer::from_bytes(b"Hello world"), 0x64ec88ca); + } + + #[test] + fn compute_key_works_correct() { + assert_eq!( + KeyComposer::compute_key("Contract", "", "balances"), + Ok(0x05e859ec) + ); + assert_eq!( + KeyComposer::compute_key("Enum", "Variant", "0"), + Ok(0x9d029590) + ); + assert_eq!( + KeyComposer::compute_key("", "Variant", "0"), + Err(Error::StructNameIsEmpty) + ); + assert_eq!( + KeyComposer::compute_key("Enum", "Variant", ""), + Err(Error::FieldNameIsEmpty) + ); } } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 3dcb1e5f452..53c0f414470 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -24,6 +24,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod key; +pub mod traits; pub use self::key::{ Key, diff --git a/crates/primitives/src/traits.rs b/crates/primitives/src/traits.rs new file mode 100644 index 00000000000..513a4f5e84e --- /dev/null +++ b/crates/primitives/src/traits.rs @@ -0,0 +1,39 @@ +// 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. + +pub use ink_primitives_derive::Storable; + +/// Trait for representing types which can be read and written to storage. +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; +} + +impl

Storable for P +where + P: scale::Encode + scale::Decode, +{ + #[inline] + fn encode(&self, dest: &mut T) { + scale::Encode::encode_to(self, dest) + } + + #[inline] + fn decode(input: &mut I) -> Result { + scale::Decode::decode(input) + } +} diff --git a/crates/storage/codegen/src/lib.rs b/crates/storage/codegen/src/lib.rs index 9c82e7fc641..5ab54072f8c 100644 --- a/crates/storage/codegen/src/lib.rs +++ b/crates/storage/codegen/src/lib.rs @@ -20,7 +20,8 @@ use syn::Data; /// **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`. + /// The salt is any generic that has bound `StorageKey`. + /// In most cases it is parent storage key or auto-generated storage key. fn find_salt(&self) -> Option; /// Return all types of the input. @@ -36,7 +37,7 @@ impl DeriveUtils for syn::DeriveInput { if let syn::TypeParamBound::Trait(trait_bound) = bound { let segments = &trait_bound.path.segments; if !segments.is_empty() - && segments.last().unwrap().ident == "KeyHolder" + && segments.last().unwrap().ident == "StorageKey" { return Some(type_param.clone()) } diff --git a/crates/storage/derive/src/item.rs b/crates/storage/derive/src/item.rs index 13a369300ef..a870d5b8e96 100644 --- a/crates/storage/derive/src/item.rs +++ b/crates/storage/derive/src/item.rs @@ -29,9 +29,9 @@ fn item_inner(s: synstructure::Structure) -> TokenStream2 { 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()); + generics.params.push( + parse2(quote! { #salt_ident : ::ink_storage::traits::StorageKey }).unwrap(), + ); let (impl_generics, _, where_clause) = generics.split_for_impl(); let (_, ty_generics_original, _) = s.ast().generics.split_for_impl(); diff --git a/crates/storage/derive/src/key_holder.rs b/crates/storage/derive/src/key_holder.rs index 106950477e1..70aba6e1b89 100644 --- a/crates/storage/derive/src/key_holder.rs +++ b/crates/storage/derive/src/key_holder.rs @@ -30,8 +30,8 @@ pub fn key_holder_derive(mut s: synstructure::Structure) -> TokenStream2 { }; s.gen_impl(quote! { - gen impl ::ink_storage::traits::KeyHolder for @Self { - const KEY: ::ink_primitives::Key = <#salt as ::ink_storage::traits::KeyHolder>::KEY; + gen impl ::ink_storage::traits::StorageKey for @Self { + const KEY: ::ink_primitives::Key = <#salt as ::ink_storage::traits::StorageKey>::KEY; } }) } diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index f48954afede..399ae9dfa65 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -21,7 +21,6 @@ extern crate proc_macro; mod item; mod key_holder; -mod storable; mod storage_layout; #[cfg(test)] @@ -30,28 +29,8 @@ mod tests; use self::{ item::item_derive, key_holder::key_holder_derive, - storable::storable_derive, storage_layout::storage_layout_derive, }; -synstructure::decl_derive!( - [Storable] => - /// Derives `ink_storage`'s `Storable` trait for the given `struct`, `enum` or `union`. - /// - /// # Examples - /// - /// ``` - /// use ink_storage::traits::Storable; - /// - /// #[derive(Storable)] - /// struct NamedFields { - /// a: u32, - /// b: [u32; 1], - /// } - /// - /// let value = ::decode(&mut &[123, 123][..]); - /// ``` - storable_derive -); synstructure::decl_derive!( [Item] => /// Derives `ink_storage`'s `Item` trait for the given `struct` or `enum`. @@ -61,7 +40,7 @@ synstructure::decl_derive!( /// ``` /// use ink_storage::traits::{ /// Item, - /// KeyHolder, + /// StorageKey, /// AutoItem, /// AutoKey, /// ManualKey, @@ -77,8 +56,8 @@ synstructure::decl_derive!( /// let _: NamedFields = >::Type::default(); /// let _: NamedFields = >>::Type::default(); /// - /// #[derive(Item, KeyHolder, Storable)] - /// struct NamedFieldsStorage { + /// #[derive(Item, StorageKey, Storable)] + /// struct NamedFieldsStorage { /// a: >>::Type, /// b: <[u32; 32] as AutoItem>>::Type, /// } @@ -91,36 +70,36 @@ synstructure::decl_derive!( item_derive ); synstructure::decl_derive!( - [KeyHolder] => - /// Derives `ink_storage`'s `KeyHolder` trait for the given `struct` or `enum`. + [StorageKey] => + /// Derives `ink_storage`'s `StorageKey` trait for the given `struct` or `enum`. /// /// # Examples /// /// ``` /// use ink_storage::traits::{ /// AutoItem, - /// KeyHolder, + /// StorageKey, /// ManualKey, /// AutoKey, /// }; /// - /// #[derive(KeyHolder)] + /// #[derive(StorageKey)] /// struct NamedFields { /// a: u32, /// b: [u32; 32], /// } /// - /// assert_eq!(::KEY, 0); + /// assert_eq!(::KEY, 0); /// - /// #[derive(KeyHolder)] - /// struct NamedFieldsManualKey { + /// #[derive(StorageKey)] + /// 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); + /// assert_eq!( as StorageKey>::KEY, 0); + /// assert_eq!( as StorageKey>::KEY, 0); + /// assert_eq!(> as StorageKey>::KEY, 123); /// ``` key_holder_derive ); diff --git a/crates/storage/derive/src/tests/item.rs b/crates/storage/derive/src/tests/item.rs index 31bf49b9108..b30e336e256 100644 --- a/crates/storage/derive/src/tests/item.rs +++ b/crates/storage/derive/src/tests/item.rs @@ -22,7 +22,7 @@ fn unit_struct_works() { } expands to { const _: () = { - impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct { type Type = UnitStruct; @@ -38,13 +38,13 @@ fn unit_struct_works() { fn unit_struct_salt_works() { crate::test_derive! { item_derive { - struct UnitStruct; + struct UnitStruct; } expands to { const _: () = { impl< - Salt: ::ink_storage::traits::KeyHolder, - __ink_generic_salt: ::ink_storage::traits::KeyHolder + Salt: ::ink_storage::traits::StorageKey, + __ink_generic_salt: ::ink_storage::traits::StorageKey > ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct { @@ -69,7 +69,7 @@ fn struct_works() { } expands to { const _: () = { - impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields { type Type = NamedFields; @@ -85,7 +85,7 @@ fn struct_works() { fn struct_salt_works() { crate::test_derive! { item_derive { - struct NamedFields { + struct NamedFields { a: i32, b: [u8; 32], d: Box, @@ -94,8 +94,8 @@ fn struct_salt_works() { expands to { const _: () = { impl< - Salt: KeyHolder, - __ink_generic_salt: ::ink_storage::traits::KeyHolder + Salt: StorageKey, + __ink_generic_salt: ::ink_storage::traits::StorageKey > ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields { @@ -120,7 +120,7 @@ fn enum_works() { } expands to { const _: () = { - impl<__ink_generic_salt: ::ink_storage::traits::KeyHolder> + impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum { type Type = MixedEnum; @@ -136,7 +136,7 @@ fn enum_works() { fn enum_salt_works() { crate::test_derive! { item_derive { - enum MixedEnum { + enum MixedEnum { A, B(u32, [u8; 32]), C { a: i32, b: (bool, i32) }, @@ -145,8 +145,8 @@ fn enum_salt_works() { expands to { const _: () = { impl< - Salt: traits::KeyHolder, - __ink_generic_salt: ::ink_storage::traits::KeyHolder + Salt: traits::StorageKey, + __ink_generic_salt: ::ink_storage::traits::StorageKey > ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum { diff --git a/crates/storage/derive/src/tests/key_holder.rs b/crates/storage/derive/src/tests/key_holder.rs index a1575b0e8e6..8a09409177f 100644 --- a/crates/storage/derive/src/tests/key_holder.rs +++ b/crates/storage/derive/src/tests/key_holder.rs @@ -22,8 +22,8 @@ fn unit_struct_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for UnitStruct { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for UnitStruct { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -39,8 +39,8 @@ fn unit_struct_generic_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for UnitStruct { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for UnitStruct { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -52,12 +52,12 @@ fn unit_struct_generic_works() { fn unit_struct_salt_works() { crate::test_derive! { key_holder_derive { - struct UnitStruct; + struct UnitStruct; } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for UnitStruct { - const KEY: ::ink_primitives::Key = ::KEY; + impl ::ink_storage::traits::StorageKey for UnitStruct { + const KEY: ::ink_primitives::Key = ::KEY; } }; } @@ -77,8 +77,8 @@ fn struct_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for NamedFields { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for NamedFields { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -98,8 +98,8 @@ fn struct_generic_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for NamedFields { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for NamedFields { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -111,7 +111,7 @@ fn struct_generic_works() { fn struct_salt_works() { crate::test_derive! { key_holder_derive { - struct NamedFields { + struct NamedFields { a: i32, b: [u8; 32], d: Box, @@ -119,8 +119,8 @@ fn struct_salt_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for NamedFields { - const KEY: ::ink_primitives::Key = ::KEY; + impl ::ink_storage::traits::StorageKey for NamedFields { + const KEY: ::ink_primitives::Key = ::KEY; } }; } @@ -140,8 +140,8 @@ fn enum_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for MixedEnum { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for MixedEnum { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -161,8 +161,8 @@ fn enum_generic_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for MixedEnum { - const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::KeyHolder>::KEY; + impl ::ink_storage::traits::StorageKey for MixedEnum { + const KEY: ::ink_primitives::Key = <() as ::ink_storage::traits::StorageKey>::KEY; } }; } @@ -174,7 +174,7 @@ fn enum_generic_works() { fn enum_salt_works() { crate::test_derive! { key_holder_derive { - enum MixedEnum { + enum MixedEnum { A, B(u32, [u8; 32]), C { a: i32, b: (bool, i32) }, @@ -182,8 +182,8 @@ fn enum_salt_works() { } expands to { const _: () = { - impl ::ink_storage::traits::KeyHolder for MixedEnum { - const KEY: ::ink_primitives::Key = ::KEY; + impl ::ink_storage::traits::StorageKey for MixedEnum { + const KEY: ::ink_primitives::Key = ::KEY; } }; } diff --git a/crates/storage/derive/src/tests/mod.rs b/crates/storage/derive/src/tests/mod.rs index 6d8e5814efc..f3db5831ea5 100644 --- a/crates/storage/derive/src/tests/mod.rs +++ b/crates/storage/derive/src/tests/mod.rs @@ -14,7 +14,6 @@ mod item; mod key_holder; -mod storable; mod storage_layout; #[macro_export] diff --git a/crates/storage/src/lazy/mapping.rs b/crates/storage/src/lazy/mapping.rs index 077c2eb8328..15f7bb891b3 100644 --- a/crates/storage/src/lazy/mapping.rs +++ b/crates/storage/src/lazy/mapping.rs @@ -22,12 +22,14 @@ use crate::traits::{ AutoKey, Item, - KeyHolder, Packed, - Storable, + StorageKey, }; use core::marker::PhantomData; -use ink_primitives::Key; +use ink_primitives::{ + traits::Storable, + Key, +}; use scale::{ Encode, Error, @@ -40,9 +42,10 @@ use scale::{ /// # Important /// /// 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). +/// the is automatically calculated using [`AutoKey`](crate::traits::AutoKey) during compilation. +/// However, anyone can specify a storage key using [`ManualKey`](crate::traits::ManualKey). +/// Specifying the storage key can be helpful for upgradeable contracts or you want to be resistant +/// to future changes of storage key calculation strategy. /// /// This is an example of how you can do this: /// ```rust @@ -60,23 +63,17 @@ use scale::{ /// #[ink(storage)] /// #[derive(Default)] /// pub struct MyContract { -/// balances: Mapping, -/// allowance: Mapping>, +/// balances: Mapping>, /// } /// /// 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(); /// let value: Balance = Default::default(); -/// self.balances.insert(&caller, &value); +/// instance.balances.insert(&caller, &value); +/// instance /// } /// /// # #[ink(message)] @@ -87,7 +84,7 @@ use scale::{ /// /// 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 { +pub struct Mapping { #[allow(clippy::type_complexity)] _marker: PhantomData (K, V, KeyType)>, } @@ -96,7 +93,7 @@ pub struct Mapping { impl Default for Mapping where V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { fn default() -> Self { Self { @@ -108,7 +105,7 @@ where impl Mapping where V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { /// Creates a new empty `Mapping`. pub fn new() -> Self { @@ -121,7 +118,7 @@ where impl ::core::fmt::Debug for Mapping where V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Mapping") @@ -134,14 +131,14 @@ impl Mapping where K: Encode, V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { /// 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, + R: Storable + scale::EncodeLike, { ink_env::set_contract_storage(&(&KeyType::KEY, key), value); } @@ -153,7 +150,7 @@ where pub fn insert_return_size(&mut self, key: Q, value: &R) -> Option where Q: scale::EncodeLike, - R: scale::EncodeLike, + R: Storable + scale::EncodeLike, { ink_env::set_contract_storage(&(&KeyType::KEY, key), value) } @@ -166,8 +163,8 @@ where where Q: scale::EncodeLike, { - ink_env::get_contract_storage::<(&Key, Q), V>(&(&KeyType::KEY, key)) - .unwrap_or_else(|error| panic!("failed to get value in mapping: {:?}", error)) + ink_env::get_contract_storage(&(&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. @@ -205,7 +202,7 @@ where impl Storable for Mapping where V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { #[inline] fn encode(&self, _dest: &mut T) {} @@ -216,20 +213,20 @@ where } } -impl Item for Mapping +impl Item for Mapping where V: Packed, - Salt: KeyHolder, - InnerSalt: KeyHolder, + Key: StorageKey, + InnerKey: StorageKey, { - type Type = Mapping; - type PreferredKey = InnerSalt; + type Type = Mapping; + type PreferredKey = InnerKey; } -impl KeyHolder for Mapping +impl StorageKey for Mapping where V: Packed, - KeyType: KeyHolder, + KeyType: StorageKey, { const KEY: Key = KeyType::KEY; } @@ -247,7 +244,7 @@ const _: () = { where K: scale_info::TypeInfo + 'static, V: Packed + StorageLayout + scale_info::TypeInfo + 'static, - KeyType: KeyHolder + scale_info::TypeInfo + 'static, + KeyType: StorageKey + scale_info::TypeInfo + 'static, { fn layout(_: &Key) -> Layout { Layout::Root(RootLayout::new( diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index 8c746a3211b..8b5f5947536 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -15,9 +15,6 @@ //! 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. @@ -27,31 +24,34 @@ mod mapping; pub use self::mapping::Mapping; use crate::traits::{ - pull_storage, push_storage, AutoKey, - DecodeWrapper, Item, - KeyHolder, - Storable, + StorageKey, }; use core::marker::PhantomData; -use ink_primitives::Key; +use ink_primitives::{ + traits::Storable, + 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`. +/// A simple wrapper around a type to store it in a separate storage cell under its own storage key. +/// If you want to update the value, first you need to [`get`](crate::Lazy::get) it, update the +/// value, and then call [`set`](crate::Lazy::set) with the new value. /// /// # 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). +/// The wrapper requires its own pre-defined storage key in order to determine where it stores +/// value. By default, the is automatically calculated using [`AutoKey`](crate::traits::AutoKey) +/// during compilation. However, anyone can specify a storage key using +/// [`ManualKey`](crate::traits::ManualKey). Specifying the storage key can be helpful for +/// upgradeable contracts or you want to be resistant to future changes of storage +/// key calculation strategy. /// /// This is an example of how you can do this: /// ```rust @@ -77,15 +77,10 @@ use scale::{ /// #[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); +/// instance.owner.set(&caller); +/// instance.balance.set(&123456); +/// instance /// } /// /// # #[ink(message)] @@ -94,14 +89,14 @@ use scale::{ /// # } /// ``` #[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub struct Lazy { +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, + KeyType: StorageKey, { fn default() -> Self { Self { @@ -112,7 +107,7 @@ where impl Lazy where - KeyType: KeyHolder, + KeyType: StorageKey, { /// Creates a new empty `Lazy`. pub fn new() -> Self { @@ -124,7 +119,7 @@ where impl core::fmt::Debug for Lazy where - KeyType: KeyHolder, + KeyType: StorageKey, { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Lazy").field("key", &KeyType::KEY).finish() @@ -134,27 +129,28 @@ where impl Lazy where V: Storable, - KeyType: KeyHolder, + KeyType: StorageKey, { - /// Get the `value` from the contract storage. - /// - /// Panics if no `value` exists. - pub fn get(&self) -> V { - pull_storage(&KeyType::KEY) + /// Reads the `value` from the contract storage. + pub fn get(&self) -> Option { + match ink_env::get_contract_storage::(&KeyType::KEY) { + Ok(Some(value)) => Some(value), + _ => None, + } } } impl Lazy where V: Storable + Default, - KeyType: KeyHolder, + KeyType: StorageKey, { - /// Get the `value` from the contract storage. + /// Reads 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, + match ink_env::get_contract_storage::(&KeyType::KEY) { + Ok(Some(value)) => value, _ => Default::default(), } } @@ -163,17 +159,17 @@ where impl Lazy where V: Storable, - KeyType: KeyHolder, + KeyType: StorageKey, { - /// Sets the given `value` to the contract storage. + /// Writes the given `value` to the contract storage. pub fn set(&mut self, value: &V) { - push_storage(value, &KeyType::KEY); + push_storage(&KeyType::KEY, value); } } impl Storable for Lazy where - KeyType: KeyHolder, + KeyType: StorageKey, { #[inline(always)] fn encode(&self, _dest: &mut T) {} @@ -184,19 +180,19 @@ where } } -impl Item for Lazy +impl Item for Lazy where - Salt: KeyHolder, - InnerSalt: KeyHolder, - V: Item, + Key: StorageKey, + InnerKey: StorageKey, + V: Item, { - type Type = Lazy; - type PreferredKey = InnerSalt; + type Type = Lazy; + type PreferredKey = InnerKey; } -impl KeyHolder for Lazy +impl StorageKey for Lazy where - KeyType: KeyHolder, + KeyType: StorageKey, { const KEY: Key = KeyType::KEY; } @@ -213,7 +209,7 @@ const _: () = { impl StorageLayout for Lazy where V: StorageLayout + scale_info::TypeInfo + 'static, - KeyType: KeyHolder + scale_info::TypeInfo + 'static, + KeyType: StorageKey + scale_info::TypeInfo + 'static, { fn layout(_: &Key) -> Layout { Layout::Root(RootLayout::new( @@ -234,7 +230,21 @@ mod tests { ink_env::test::run_test::(|_| { let mut storage: Lazy> = Lazy::new(); storage.set(&2); - assert_eq!(storage.get(), 2); + assert_eq!(storage.get(), Some(2)); + + Ok(()) + }) + .unwrap() + } + + #[test] + fn set_and_get_work_for_two_lazy_with_same_manual_key() { + ink_env::test::run_test::(|_| { + let mut storage: Lazy> = Lazy::new(); + storage.set(&2); + + let storage2: Lazy> = Lazy::new(); + assert_eq!(storage2.get(), Some(2)); Ok(()) }) @@ -254,7 +264,7 @@ mod tests { #[test] #[should_panic(expected = "storage entry was empty")] - fn gets_failes_if_no_key_set() { + fn gets_fails_if_no_key_set() { ink_env::test::run_test::(|_| { let storage: Lazy> = Lazy::new(); storage.get(); diff --git a/crates/storage/src/test_utils.rs b/crates/storage/src/test_utils.rs index f1b5df61132..1ce70230ef0 100644 --- a/crates/storage/src/test_utils.rs +++ b/crates/storage/src/test_utils.rs @@ -28,7 +28,7 @@ where .unwrap() } -/// Creates test to verify that the type for primitives is atomic and the same. +/// Creates test to verify that the primitive types are packed. #[macro_export] macro_rules! item_works_for_primitive { ( $ty:ty ) => { diff --git a/crates/storage/src/traits/impls/arrays.rs b/crates/storage/src/traits/impls/arrays.rs deleted file mode 100644 index c6998f2ebdb..00000000000 --- a/crates/storage/src/traits/impls/arrays.rs +++ /dev/null @@ -1,24 +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. - -#[cfg(test)] -mod tests { - use crate::item_works_for_primitive; - - type Array = [i32; 4]; - item_works_for_primitive!(Array); - - type ArrayTuples = [(i32, i32); 2]; - item_works_for_primitive!(ArrayTuples); -} diff --git a/crates/storage/src/traits/impls/mod.rs b/crates/storage/src/traits/impls/mod.rs index b36257c1424..65236bb7814 100644 --- a/crates/storage/src/traits/impls/mod.rs +++ b/crates/storage/src/traits/impls/mod.rs @@ -12,8 +12,99 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod arrays; -mod prims; -mod tuples; +use crate::traits::{ + AutoItem, + Item, + Packed, + StorageKey, +}; +use core::{ + fmt::Debug, + marker::PhantomData, +}; +use ink_primitives::{ + Key, + KeyComposer, +}; -pub(crate) mod storage; +/// Auto key type means that the storage key should be calculated automatically. +#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct AutoKey; + +impl StorageKey for AutoKey { + const KEY: Key = 0; +} + +impl Debug for AutoKey { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("AutoKey").finish() + } +} + +/// Manual key type specifies the storage key. +#[derive(Default, Copy, Clone, Eq, PartialEq, PartialOrd)] +#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] +pub struct ManualKey( + PhantomData ParentKey>, +); + +impl StorageKey for ManualKey { + const KEY: Key = KeyComposer::concat(KEY, ParentKey::KEY); +} + +impl Debug for ManualKey { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("ManualKey") + .field("key", &::KEY) + .finish() + } +} + +/// 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 StorageKey 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 }; +} + +type FinalKey = + ResolverKey<>::PreferredKey, ManualKey>; + +// `AutoItem` trait figures out that storage key it should use. +// - If the `PreferredKey` is `AutoKey` it will use an auto-generated key passed as generic +// into `AutoItem`. +// - If `PreferredKey` is `ManualKey`, then it will use it. +impl AutoItem> for T +where + T: Item, + T: Item>, + ParentKey: StorageKey, +{ + type Type = >>::Type; +} + +impl

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

StorageKey for P +where + P: Packed, +{ + const KEY: Key = 0; +} + +impl Item for P +where + P: Packed, + Key: StorageKey, +{ + type Type = P; + type PreferredKey = AutoKey; +} + +#[cfg(test)] +mod tests; diff --git a/crates/storage/src/traits/impls/storage.rs b/crates/storage/src/traits/impls/storage.rs deleted file mode 100644 index 39e222bc23d..00000000000 --- a/crates/storage/src/traits/impls/storage.rs +++ /dev/null @@ -1,107 +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::{ - 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/prims.rs b/crates/storage/src/traits/impls/tests.rs similarity index 57% rename from crates/storage/src/traits/impls/prims.rs rename to crates/storage/src/traits/impls/tests.rs index d5a54be7f6d..d05f2c8b8f3 100644 --- a/crates/storage/src/traits/impls/prims.rs +++ b/crates/storage/src/traits/impls/tests.rs @@ -1,19 +1,14 @@ -// 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. - -#[cfg(test)] -mod tests { +mod arrays { + use crate::item_works_for_primitive; + + type Array = [i32; 4]; + item_works_for_primitive!(Array); + + type ArrayTuples = [(i32, i32); 2]; + item_works_for_primitive!(ArrayTuples); +} + +mod prims { use crate::item_works_for_primitive; use ink_env::AccountId; @@ -43,3 +38,10 @@ mod tests { type BoxOptionU8 = Box>; item_works_for_primitive!(BoxOptionU8); } + +mod tuples { + use crate::item_works_for_primitive; + + type TupleSix = (i32, u32, String, u8, bool, Box>); + item_works_for_primitive!(TupleSix); +} diff --git a/crates/storage/src/traits/impls/tuples.rs b/crates/storage/src/traits/impls/tuples.rs deleted file mode 100644 index 04b65e968b5..00000000000 --- a/crates/storage/src/traits/impls/tuples.rs +++ /dev/null @@ -1,21 +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. - -#[cfg(test)] -mod tests { - use crate::item_works_for_primitive; - - type TupleSix = (i32, u32, String, u8, bool, Box>); - item_works_for_primitive!(TupleSix); -} diff --git a/crates/storage/src/traits/layout/impls.rs b/crates/storage/src/traits/layout/impls.rs index a671d8b3327..34a98176b98 100644 --- a/crates/storage/src/traits/layout/impls.rs +++ b/crates/storage/src/traits/layout/impls.rs @@ -71,7 +71,7 @@ macro_rules! impl_storage_layout_for_arrays { { fn layout(key: &Key) -> Layout { let len: u32 = $len; - // Generic type is atomic, so it doesn't take any cell + // Generic type is packed, so it doesn't take any cell Layout::Array(ArrayLayout::new( LayoutKey::from(key), len, diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index d1c985e2d64..6c1fcd02683 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -15,12 +15,14 @@ //! Traits and interfaces to operate with storage entities. //! //! Generally a type is said to be a storage entity if it implements the -//! `Item` trait. This defines certain constants and routines in order +//! `Storable` 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 `Packed` shows that the type is packed and can be stored //! into single storage cell. Some collections works only with packed structures. +//! Consequently, non-`Packed` are types that can't be stored in one cell. +//! It means that at least one of the fields has its storage cell. mod impls; mod storage; @@ -38,7 +40,7 @@ pub use self::layout::{ StorageLayout, }; pub use self::{ - impls::storage::{ + impls::{ AutoKey, ManualKey, ResolverKey, @@ -46,60 +48,37 @@ pub use self::{ storage::{ AutoItem, Item, - KeyHolder, OnCallInitializer, Packed, - Storable, + StorageKey, }, }; -use ink_primitives::Key; +use ink_primitives::{ + traits::Storable, + Key, +}; pub use ink_storage_derive::{ Item, - KeyHolder, - Storable, + StorageKey, StorageLayout, }; -use scale::{ - Decode, - Encode, -}; - -#[repr(transparent)] -pub(crate) struct DecodeWrapper(pub S); - -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 decode and its storage key. pub fn pull_storage(key: &Key) -> T where T: Storable, { - match ink_env::get_contract_storage::>(key) { - Ok(Some(wrapper)) => wrapper.0, + match ink_env::get_contract_storage::(key) { + Ok(Some(value)) => value, Ok(None) => panic!("storage entry was empty"), Err(_) => panic!("could not properly decode storage entry"), } } -#[repr(transparent)] -pub(crate) struct EncodeWrapper<'a, S: Storable>(pub &'a S); - -impl<'a, S: Storable> Encode for EncodeWrapper<'a, S> { - #[inline(always)] - fn encode_to(&self, dest: &mut T) { - self.0.encode(dest) - } -} - /// Pushes the entity to the contract storage using encode and storage key. -pub fn push_storage(entity: &T, key: &Key) -> Option +pub fn push_storage(key: &Key, entity: &T) -> Option where T: Storable, { - ink_env::set_contract_storage::>(key, &EncodeWrapper(entity)) + ink_env::set_contract_storage::(key, entity) } diff --git a/crates/storage/src/traits/pull_or_init.rs b/crates/storage/src/traits/pull_or_init.rs index e14289a786f..a4c87b1879a 100644 --- a/crates/storage/src/traits/pull_or_init.rs +++ b/crates/storage/src/traits/pull_or_init.rs @@ -12,13 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! The module helps during compilation time decide which pull mechanism to use. +//! If the type implements the` OnCallInitializer` trait, it will use `pull_storage` + +//! initialization if the pull failed. Otherwise, it will use only `pull_storage`. + use crate::traits::{ pull_storage, storage::OnCallInitializer, - DecodeWrapper, - Storable, }; -use ink_primitives::Key; +use ink_primitives::{ + traits::Storable, + Key, +}; pub struct PullOrInit { marker: core::marker::PhantomData T>, @@ -27,14 +32,14 @@ pub struct PullOrInit { impl PullOrInit { #[allow(dead_code)] pub fn pull_or_init(key: &Key) -> T { - let maybe_instance = ink_env::get_contract_storage::>(key); + 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, + Ok(Some(value)) => value, } } } @@ -86,7 +91,7 @@ mod tests { #[ink_lang::test] fn pull_or_init_works() { const KEY: Key = 111; - push_storage(&U32(456), &KEY); + push_storage(&KEY, &U32(456)); let instance = pull_or_init!(U32, KEY); // Instead of init we used a pulled value @@ -104,7 +109,7 @@ mod tests { #[ink_lang::test] fn pull_works() { const KEY: Key = 111; - push_storage(&321, &KEY); + push_storage(&KEY, &321); let instance = pull_or_init!(u32, KEY); assert_eq!(321, instance); } diff --git a/crates/storage/src/traits/storage.rs b/crates/storage/src/traits/storage.rs index 0ae594a60d8..e50c8262ddb 100644 --- a/crates/storage/src/traits/storage.rs +++ b/crates/storage/src/traits/storage.rs @@ -12,12 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use ink_primitives::Key; +use ink_primitives::{ + traits::Storable, + 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. +/// Trait for describing types that can be read and written to storage while all fields occupy +/// only a single storage cell. /// -/// All other types - **Non-Packed**. +/// If at least one of the fields in the type occupies its storage cell, this type +/// is considered non-packed. /// /// # Note /// @@ -25,31 +29,16 @@ use ink_primitives::Key; /// and `scale::Decode` via blank implementation. /// /// Don't try to implement that trait manually. -pub trait Packed: scale::Decode + scale::Encode {} +pub trait Packed: Storable + 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. +/// Holds storage key for the type. /// /// # 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 +pub trait StorageKey { + /// Storage key of the type. const KEY: Key; /// Returns the storage key. @@ -64,23 +53,22 @@ pub trait KeyHolder { /// /// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types /// via blank implementation. -pub trait Item { - /// Type with storage key inside +pub trait Item { + /// Storable type with storage key inside. type Type: Storable; - /// Preferred storage key - type PreferredKey: KeyHolder; + /// The storage key that the type prefers. It can be overwritten by an auto-generated storage key. + type PreferredKey: StorageKey; } /// 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 +/// The trait is used by codegen to determine which storage key the type should have. +pub trait AutoItem { + /// Storable 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. +/// A trait to support initialization on the runtime if it cannot pull from the storage. /// /// It can be in several cases: /// - The contract doesn't have constructor. That initializer can be alternative for the constructor. @@ -90,8 +78,8 @@ pub trait AutoItem { /// 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. + /// A default instance of the contract is first created. The initialize method + /// is then called on that instance. There are no restrictions to what a developer + /// may do during the initialization phase, including doing nothing. fn initialize(&mut self); } diff --git a/examples/upgradeable-contracts/delegate-calls/lib.rs b/examples/upgradeable-contracts/delegate-calls/lib.rs index be0a28c0af8..c71e8e92e14 100644 --- a/examples/upgradeable-contracts/delegate-calls/lib.rs +++ b/examples/upgradeable-contracts/delegate-calls/lib.rs @@ -24,8 +24,8 @@ pub mod upgradeable_contract { KeyComposer, }; use ink_storage::traits::{ - KeyHolder, ManualKey, + StorageKey, }; const PROXY_KEY: Key = KeyComposer::from_str("ProxyFields"); @@ -38,7 +38,7 @@ pub mod upgradeable_contract { /// 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> { + 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, From 7e094eba105a34477470ed181e0dc9c8091dee07 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 17:54:20 +0100 Subject: [PATCH 09/40] Make CI happy --- .config/cargo_spellcheck.dic | 1 + crates/storage/src/traits/pull_or_init.rs | 4 ++-- examples/mother/lib.rs | 8 ++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.config/cargo_spellcheck.dic b/.config/cargo_spellcheck.dic index 9a9a254ea8e..777960d2632 100644 --- a/.config/cargo_spellcheck.dic +++ b/.config/cargo_spellcheck.dic @@ -107,3 +107,4 @@ natively payability unpayable initializer +storable diff --git a/crates/storage/src/traits/pull_or_init.rs b/crates/storage/src/traits/pull_or_init.rs index a4c87b1879a..7f7f4f75cdd 100644 --- a/crates/storage/src/traits/pull_or_init.rs +++ b/crates/storage/src/traits/pull_or_init.rs @@ -13,8 +13,8 @@ // limitations under the License. //! The module helps during compilation time decide which pull mechanism to use. -//! If the type implements the` OnCallInitializer` trait, it will use `pull_storage` + -//! initialization if the pull failed. Otherwise, it will use only `pull_storage`. +//! If the type implements `OnCallInitializer` trait, it will use `pull_storage` and +//! initialization(if the pull failed). Otherwise, it will use only `pull_storage`. use crate::traits::{ pull_storage, diff --git a/examples/mother/lib.rs b/examples/mother/lib.rs index d3605ad0524..3f4898356ab 100755 --- a/examples/mother/lib.rs +++ b/examples/mother/lib.rs @@ -34,7 +34,7 @@ mod mother { /// 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, PartialEq, Debug, Clone, scale::Decode, scale::Encode)] + #[derive(Default, PartialEq, Eq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) @@ -42,7 +42,7 @@ mod mother { pub struct Bids(Vec>>); /// Auction outline. - #[derive(PartialEq, Debug, Clone, scale::Decode, scale::Encode)] + #[derive(PartialEq, Eq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) @@ -56,7 +56,7 @@ mod mother { /// Auction statuses. /// Logic inspired by /// [Parachain Auction](https://github.com/paritytech/polkadot/blob/master/runtime/common/src/traits.rs#L160) - #[derive(PartialEq, Debug, Clone, scale::Decode, scale::Encode)] + #[derive(PartialEq, Eq, Debug, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) @@ -78,7 +78,7 @@ mod mother { } /// Struct for storing auction data. - #[derive(Debug, PartialEq, Clone, scale::Decode, scale::Encode)] + #[derive(Debug, PartialEq, Eq, Clone, scale::Decode, scale::Encode)] #[cfg_attr( feature = "std", derive(ink_storage::traits::StorageLayout, scale_info::TypeInfo) From 8c9942d8331a7611d693744cc83745a25dc7cdf1 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 18:15:48 +0100 Subject: [PATCH 10/40] Fix tests --- .../fail/constructor-self-receiver-03.stderr | 27 +++++++------------ .../event-too-many-topics-anonymous.stderr | 20 +++++++------- .../fail/event-too-many-topics.stderr | 20 +++++++------- .../trait-message-selector-mismatch.stderr | 6 ++--- .../trait-message-selector-overlap-1.stderr | 4 +-- .../trait-message-selector-overlap-2.stderr | 4 +-- .../trait-message-selector-overlap-3.stderr | 4 +-- crates/storage/src/lazy/mod.rs | 3 +-- 8 files changed, 40 insertions(+), 48 deletions(-) diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr index d48721fb3db..0f050dff38c 100644 --- a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr @@ -1,9 +1,3 @@ -error[E0637]: `&` without an explicit lifetime name cannot be used here - --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:34 - | -10 | pub fn constructor(this: &Self) -> Self { - | ^ explicit lifetime name needed here - error[E0411]: cannot find type `Self` in this scope --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:35 | @@ -22,16 +16,15 @@ error[E0411]: cannot find type `Self` in this scope 10 | pub fn constructor(this: &Self) -> Self { | ^^^^ `Self` is only available in impls, traits, and type definitions -error[E0277]: the trait bound `&'static Contract: WrapperTypeDecode` is not satisfied - --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:9 +error[E0106]: missing lifetime specifier + --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:34 + | +10 | pub fn constructor(this: &Self) -> Self { + | ^ expected named lifetime parameter + | +help: consider introducing a named lifetime parameter | -10 | / pub fn constructor(this: &Self) -> Self { -11 | | Self {} -12 | | } - | |_________^ the trait `WrapperTypeDecode` is not implemented for `&'static Contract` +10 ~ pub fn constructor(this: &'a Self) -> Self { +11 | Self {} +12 ~ }<'a> | - = help: the following other types implement trait `WrapperTypeDecode`: - Arc - Box - Rc - = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `&'static Contract` diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr index d865891b818..f33a62ce9ad 100644 --- a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `EventTopics<4>: RespectTopicLimit<2>` is not satisfied +error[E0277]: the trait bound `EventTopics<4_usize>: RespectTopicLimit<2_usize>` is not satisfied --> tests/ui/contract/fail/event-too-many-topics-anonymous.rs:26:5 | 26 | / pub struct Event { @@ -8,17 +8,17 @@ error[E0277]: the trait bound `EventTopics<4>: RespectTopicLimit<2>` is not sati ... | 34 | | arg_4: i32, 35 | | } - | |_____^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<4>` + | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<4_usize>` | = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<0>> - as RespectTopicLimit<10>> - as RespectTopicLimit<11>> - as RespectTopicLimit<12>> - as RespectTopicLimit<1>> - as RespectTopicLimit<2>> - as RespectTopicLimit<3>> - as RespectTopicLimit<4>> + as RespectTopicLimit<0_usize>> + as RespectTopicLimit<10_usize>> + as RespectTopicLimit<11_usize>> + as RespectTopicLimit<12_usize>> + as RespectTopicLimit<1_usize>> + as RespectTopicLimit<2_usize>> + as RespectTopicLimit<3_usize>> + as RespectTopicLimit<4_usize>> and 83 others note: required by a bound in `EventRespectsTopicLimit` --> src/codegen/event/topics.rs diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr index fab37ceb0f1..390b822b341 100644 --- a/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `EventTopics<3>: RespectTopicLimit<2>` is not satisfied +error[E0277]: the trait bound `EventTopics<3_usize>: RespectTopicLimit<2_usize>` is not satisfied --> tests/ui/contract/fail/event-too-many-topics.rs:26:5 | 26 | / pub struct Event { @@ -8,17 +8,17 @@ error[E0277]: the trait bound `EventTopics<3>: RespectTopicLimit<2>` is not sati ... | 32 | | arg_3: i32, 33 | | } - | |_____^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<3>` + | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<3_usize>` | = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<0>> - as RespectTopicLimit<10>> - as RespectTopicLimit<11>> - as RespectTopicLimit<12>> - as RespectTopicLimit<1>> - as RespectTopicLimit<2>> - as RespectTopicLimit<3>> - as RespectTopicLimit<4>> + as RespectTopicLimit<0_usize>> + as RespectTopicLimit<10_usize>> + as RespectTopicLimit<11_usize>> + as RespectTopicLimit<12_usize>> + as RespectTopicLimit<1_usize>> + as RespectTopicLimit<2_usize>> + as RespectTopicLimit<3_usize>> + as RespectTopicLimit<4_usize>> and 83 others note: required by a bound in `EventRespectsTopicLimit` --> src/codegen/event/topics.rs diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr index c1070999dba..5e93d3f7594 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> tests/ui/contract/fail/trait-message-selector-mismatch.rs:25:9 | 25 | fn message(&self) {} - | ^^^^^^^^^^^^^^^^^^^^ expected `1`, found `2` + | ^^^^^^^^^^^^^^^^^^^^ expected `1_u32`, found `2_u32` | - = note: expected struct `TraitMessageSelector<1>` - found struct `TraitMessageSelector<2>` + = note: expected struct `TraitMessageSelector<1_u32>` + found struct `TraitMessageSelector<2_u32>` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr index c321b5c7ab8..c13a3c9c6e8 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1083895717>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1083895717_u32>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:47:9 | 42 | fn message(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1083895717>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1083895717_u32>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr index 9c39a2aa537..c41dd6584e8 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1518209067>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1518209067_u32>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:47:9 | 42 | fn message(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1518209067>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1518209067_u32>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr index e8141dfb3dd..f8aca9c0319 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<42>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<42_u32>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:47:9 | 42 | fn message1(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message2(&self) {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<42>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<42_u32>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index 8b5f5947536..35558207146 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -263,11 +263,10 @@ mod tests { } #[test] - #[should_panic(expected = "storage entry was empty")] fn gets_fails_if_no_key_set() { ink_env::test::run_test::(|_| { let storage: Lazy> = Lazy::new(); - storage.get(); + assert_eq!(storage.get(), None); Ok(()) }) From 9188ca74eacc48a617382abd41467757f211ee08 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 18:30:51 +0100 Subject: [PATCH 11/40] Fix tests --- crates/storage/derive/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index 399ae9dfa65..18a59e5864f 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -44,8 +44,8 @@ synstructure::decl_derive!( /// AutoItem, /// AutoKey, /// ManualKey, - /// Storable, /// }; + /// use ink_primitives::traits::Storable; /// /// #[derive(Default, Item, Storable)] /// struct NamedFields { From 7375b4c174c901b28eb6700f99339e53c1f02f14 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 19:54:33 +0100 Subject: [PATCH 12/40] Fix tests --- .../fail/constructor-self-receiver-03.stderr | 27 ++++++++++++------- .../event-too-many-topics-anonymous.stderr | 20 +++++++------- .../fail/event-too-many-topics.stderr | 20 +++++++------- .../trait-message-selector-mismatch.stderr | 6 ++--- .../trait-message-selector-overlap-1.stderr | 4 +-- .../trait-message-selector-overlap-2.stderr | 4 +-- .../trait-message-selector-overlap-3.stderr | 4 +-- .../fail/collections_only_packed_1.stderr | 12 ++++----- .../fail/collections_only_packed_2.stderr | 12 ++++----- 9 files changed, 58 insertions(+), 51 deletions(-) diff --git a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr index 0f050dff38c..d48721fb3db 100644 --- a/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr +++ b/crates/lang/tests/ui/contract/fail/constructor-self-receiver-03.stderr @@ -1,3 +1,9 @@ +error[E0637]: `&` without an explicit lifetime name cannot be used here + --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:34 + | +10 | pub fn constructor(this: &Self) -> Self { + | ^ explicit lifetime name needed here + error[E0411]: cannot find type `Self` in this scope --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:35 | @@ -16,15 +22,16 @@ error[E0411]: cannot find type `Self` in this scope 10 | pub fn constructor(this: &Self) -> Self { | ^^^^ `Self` is only available in impls, traits, and type definitions -error[E0106]: missing lifetime specifier - --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:34 - | -10 | pub fn constructor(this: &Self) -> Self { - | ^ expected named lifetime parameter - | -help: consider introducing a named lifetime parameter +error[E0277]: the trait bound `&'static Contract: WrapperTypeDecode` is not satisfied + --> tests/ui/contract/fail/constructor-self-receiver-03.rs:10:9 | -10 ~ pub fn constructor(this: &'a Self) -> Self { -11 | Self {} -12 ~ }<'a> +10 | / pub fn constructor(this: &Self) -> Self { +11 | | Self {} +12 | | } + | |_________^ the trait `WrapperTypeDecode` is not implemented for `&'static Contract` | + = help: the following other types implement trait `WrapperTypeDecode`: + Arc + Box + Rc + = note: required because of the requirements on the impl of `parity_scale_codec::Decode` for `&'static Contract` diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr index f33a62ce9ad..d865891b818 100644 --- a/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics-anonymous.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `EventTopics<4_usize>: RespectTopicLimit<2_usize>` is not satisfied +error[E0277]: the trait bound `EventTopics<4>: RespectTopicLimit<2>` is not satisfied --> tests/ui/contract/fail/event-too-many-topics-anonymous.rs:26:5 | 26 | / pub struct Event { @@ -8,17 +8,17 @@ error[E0277]: the trait bound `EventTopics<4_usize>: RespectTopicLimit<2_usize>` ... | 34 | | arg_4: i32, 35 | | } - | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<4_usize>` + | |_____^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<4>` | = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<0_usize>> - as RespectTopicLimit<10_usize>> - as RespectTopicLimit<11_usize>> - as RespectTopicLimit<12_usize>> - as RespectTopicLimit<1_usize>> - as RespectTopicLimit<2_usize>> - as RespectTopicLimit<3_usize>> - as RespectTopicLimit<4_usize>> + as RespectTopicLimit<0>> + as RespectTopicLimit<10>> + as RespectTopicLimit<11>> + as RespectTopicLimit<12>> + as RespectTopicLimit<1>> + as RespectTopicLimit<2>> + as RespectTopicLimit<3>> + as RespectTopicLimit<4>> and 83 others note: required by a bound in `EventRespectsTopicLimit` --> src/codegen/event/topics.rs diff --git a/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr index 390b822b341..fab37ceb0f1 100644 --- a/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr +++ b/crates/lang/tests/ui/contract/fail/event-too-many-topics.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `EventTopics<3_usize>: RespectTopicLimit<2_usize>` is not satisfied +error[E0277]: the trait bound `EventTopics<3>: RespectTopicLimit<2>` is not satisfied --> tests/ui/contract/fail/event-too-many-topics.rs:26:5 | 26 | / pub struct Event { @@ -8,17 +8,17 @@ error[E0277]: the trait bound `EventTopics<3_usize>: RespectTopicLimit<2_usize>` ... | 32 | | arg_3: i32, 33 | | } - | |_____^ the trait `RespectTopicLimit<2_usize>` is not implemented for `EventTopics<3_usize>` + | |_____^ the trait `RespectTopicLimit<2>` is not implemented for `EventTopics<3>` | = help: the following other types implement trait `RespectTopicLimit`: - as RespectTopicLimit<0_usize>> - as RespectTopicLimit<10_usize>> - as RespectTopicLimit<11_usize>> - as RespectTopicLimit<12_usize>> - as RespectTopicLimit<1_usize>> - as RespectTopicLimit<2_usize>> - as RespectTopicLimit<3_usize>> - as RespectTopicLimit<4_usize>> + as RespectTopicLimit<0>> + as RespectTopicLimit<10>> + as RespectTopicLimit<11>> + as RespectTopicLimit<12>> + as RespectTopicLimit<1>> + as RespectTopicLimit<2>> + as RespectTopicLimit<3>> + as RespectTopicLimit<4>> and 83 others note: required by a bound in `EventRespectsTopicLimit` --> src/codegen/event/topics.rs diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr index 5e93d3f7594..c1070999dba 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-mismatch.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> tests/ui/contract/fail/trait-message-selector-mismatch.rs:25:9 | 25 | fn message(&self) {} - | ^^^^^^^^^^^^^^^^^^^^ expected `1_u32`, found `2_u32` + | ^^^^^^^^^^^^^^^^^^^^ expected `1`, found `2` | - = note: expected struct `TraitMessageSelector<1_u32>` - found struct `TraitMessageSelector<2_u32>` + = note: expected struct `TraitMessageSelector<1>` + found struct `TraitMessageSelector<2>` diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr index c13a3c9c6e8..c321b5c7ab8 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-1.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1083895717_u32>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1083895717>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:47:9 | 42 | fn message(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1083895717_u32>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1083895717>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-1.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr index c41dd6584e8..9c39a2aa537 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-2.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1518209067_u32>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<1518209067>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:47:9 | 42 | fn message(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message(&self) {} | ^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1518209067_u32>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<1518209067>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-2.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { diff --git a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr index f8aca9c0319..e8141dfb3dd 100644 --- a/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr +++ b/crates/lang/tests/ui/contract/fail/trait-message-selector-overlap-3.stderr @@ -1,4 +1,4 @@ -error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<42_u32>` for type `contract::Contract` +error[E0119]: conflicting implementations of trait `ink_lang::reflect::DispatchableMessageInfo<42>` for type `contract::Contract` --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:47:9 | 42 | fn message1(&self) {} @@ -7,7 +7,7 @@ error[E0119]: conflicting implementations of trait `ink_lang::reflect::Dispatcha 47 | fn message2(&self) {} | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `contract::Contract` -error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<42_u32>` for type `contract::_::CallBuilder` +error[E0119]: conflicting implementations of trait `ink_lang::codegen::TraitCallForwarderFor<42>` for type `contract::_::CallBuilder` --> tests/ui/contract/fail/trait-message-selector-overlap-3.rs:45:5 | 40 | / impl TraitDefinition1 for Contract { 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 index 47d9542a648..bc77a1650c3 100644 --- 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 @@ -7,7 +7,7 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied = 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 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 @@ -21,7 +21,7 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied = 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 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 @@ -32,7 +32,7 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied = 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 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 | @@ -57,7 +57,7 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied = 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 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 | @@ -79,7 +79,7 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied = 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 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 | @@ -104,7 +104,7 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied = 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 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 | 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 index d9c1faca002..ca4ecbe9b2a 100644 --- 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 @@ -7,7 +7,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 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 @@ -18,7 +18,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 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 @@ -29,7 +29,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 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 | @@ -51,7 +51,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 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 | @@ -73,7 +73,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 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 | @@ -95,7 +95,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 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 | From 906b1d8e0b179daf5427bd009b5c39349944ef6b Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 15 Aug 2022 20:08:02 +0100 Subject: [PATCH 13/40] Fix tests --- .../tests/ui/contract/fail/message-returns-non-codec.stderr | 2 +- .../tests/ui/trait_def/fail/message_output_non_codec.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 0a644b1c6f9..31684c857ca 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.2/src/codec.rs + --> $CARGO/parity-scale-codec-3.1.5/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^ 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 635afb419e1..073ed0e683b 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.2/src/codec.rs + --> $CARGO/parity-scale-codec-3.1.5/src/codec.rs | | pub trait Decode: Sized { | ^^^^^^^^^^^^^^^^^^^^^^^ From 290b38427c847f74583257264f899a50aba743f6 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Tue, 16 Aug 2022 09:59:03 +0100 Subject: [PATCH 14/40] Apply suggestions: - In most cases it is comments=) - Moved `pull_or_init` on one level upper. - Put the tests into the `impls/mod.rs` --- crates/primitives/derive/LICENSE | 204 +----------- crates/primitives/derive/README.md | 290 +----------------- crates/primitives/src/key.rs | 1 + crates/primitives/src/traits.rs | 6 + crates/storage/src/lazy/mod.rs | 33 +- crates/storage/src/lib.rs | 5 +- .../storage/src/{traits => }/pull_or_init.rs | 14 +- crates/storage/src/traits/impls/mod.rs | 54 +++- crates/storage/src/traits/impls/tests.rs | 47 --- crates/storage/src/traits/mod.rs | 23 +- 10 files changed, 102 insertions(+), 575 deletions(-) mode change 100644 => 120000 crates/primitives/derive/LICENSE mode change 100644 => 120000 crates/primitives/derive/README.md rename crates/storage/src/{traits => }/pull_or_init.rs (83%) delete mode 100644 crates/storage/src/traits/impls/tests.rs diff --git a/crates/primitives/derive/LICENSE b/crates/primitives/derive/LICENSE deleted file mode 100644 index 6b0b1270ff0..00000000000 --- a/crates/primitives/derive/LICENSE +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - diff --git a/crates/primitives/derive/LICENSE b/crates/primitives/derive/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/crates/primitives/derive/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/crates/primitives/derive/README.md b/crates/primitives/derive/README.md deleted file mode 100644 index 5e0e91bbc8b..00000000000 --- a/crates/primitives/derive/README.md +++ /dev/null @@ -1,289 +0,0 @@ -

- ink! -

- Parity's ink! for writing smart contracts -

- -[![linux][a1]][a2] [![codecov][c1]][c2] [![coveralls][d1]][d2] [![loc][e1]][e2] [![stack-exchange][s1]][s2] - -[a1]: https://gitlab.parity.io/parity/ink/badges/master/pipeline.svg -[a2]: https://gitlab.parity.io/parity/ink/pipelines?ref=master -[c1]: https://codecov.io/gh/paritytech/ink/branch/master/graph/badge.svg -[c2]: https://codecov.io/gh/paritytech/ink/branch/master -[d1]: https://coveralls.io/repos/github/paritytech/ink/badge.svg?branch=master -[d2]: https://coveralls.io/github/paritytech/ink?branch=master -[e1]: https://tokei.rs/b1/github/paritytech/ink?category=code -[e2]: https://github.com/Aaronepower/tokei#badges -[f1]: https://img.shields.io/badge/click-blue.svg -[f2]: https://paritytech.github.io/ink/ink_storage -[g1]: https://img.shields.io/badge/click-blue.svg -[g2]: https://paritytech.github.io/ink/ink_env -[i1]: https://img.shields.io/badge/click-blue.svg -[i2]: https://paritytech.github.io/ink/ink_prelude -[j1]: https://img.shields.io/badge/click-blue.svg -[j2]: https://paritytech.github.io/ink/ink_lang -[k1]: https://img.shields.io/badge/matrix-chat-brightgreen.svg?style=flat -[k2]: https://riot.im/app/#/room/#ink:matrix.parity.io -[l1]: https://img.shields.io/discord/722223075629727774?style=flat-square&label=discord -[l2]: https://discord.com/invite/wGUDt2p -[s1]: https://img.shields.io/badge/click-white.svg?logo=StackExchange&label=ink!%20Support%20on%20StackExchange&labelColor=white&color=blue -[s2]: https://substrate.stackexchange.com/questions/tagged/ink?tab=Votes - -> squink, the ink! mascotink! is an [eDSL](https://wiki.haskell.org/Embedded_domain_specific_language) to write smart contracts in Rust for blockchains built on the [Substrate](https://github.com/paritytech/substrate) framework. ink! contracts are compiled to WebAssembly. - -
- -[Guided Tutorial for Beginners](https://docs.substrate.io/tutorials/v3/ink-workshop/pt1)  •   -[ink! Documentation Portal](https://ink.substrate.io)  •   -[Developer Documentation](https://paritytech.github.io/ink/ink_lang/) - - -
-
- -More relevant links: -* [Substrate Stack Exchange](https://substrate.stackexchange.com/questions/tagged/ink?tab=Votes) ‒ Forum for getting your ink! questions answered -* [`cargo-contract`](https://github.com/paritytech/cargo-contract) ‒ CLI tool for ink! contracts -* [Contracts UI](https://contracts-ui.substrate.io/) ‒ Frontend for contract instantiation and interaction -* [Substrate Contracts Node](https://github.com/paritytech/substrate-contracts-node) ‒ Simple Substrate blockchain which includes smart contract functionality -* We post announcements on [Matrix][k2] and [Discord][l2] (in the - [`ink_smart-contracts`](https://discord.com/channels/722223075629727774/765280480609828864) channel). - - -## Table of Contents - -* [Play with It](#play-with-it) -* [Usage](#usage) -* [Hello, World! ‒ The Flipper](#hello-world--the-flipper) -* [Examples](#examples) -* [How it Works](#how-it-works) -* [ink! Macros & Attributes Overview](#ink-macros--attributes-overview) - * [Entry Point](#entry-point) - * [Trait Definitions](#trait-definitions) - * [Off-chain Testing](#off-chain-testing) -* [Developer Documentation](#developer-documentation) -* [Community Badges](#community-badges) -* [Contributing](#contributing) -* [License](#license) - - -## Play with It - -If you want to have a local setup you can use our [`substrate-contracts-node`](https://github.com/paritytech/substrate-contracts-node) for a quickstart. -It's a simple Substrate blockchain which includes the Substrate module for smart contract functionality ‒ the `contracts` pallet (see [How it Works](#how-it-works) for more). - -We also have a live testnet on [Rococo](https://github.com/paritytech/cumulus/#rococo-) -called [Canvas](https://ink.substrate.io/canvas). Canvas is a Substrate based -parachain which supports ink! smart contracts. For further instructions on using this -testnet, follow the instructions in the -[our documentation](https://ink.substrate.io/canvas#rococo-deployment). - -For both types of chains the [Contracts UI](https://contracts-ui.substrate.io/) -can be used to instantiate your contract to a chain and interact with it. - -## Usage - -A prerequisite for compiling smart contracts is to have Rust and Cargo installed. Here's [an installation guide](https://doc.rust-lang.org/cargo/getting-started/installation.html). - -We recommend installing [`cargo-contract`](https://github.com/paritytech/cargo-contract) as well. -It's a CLI tool which helps set up and manage WebAssembly smart contracts written with ink!: - -``` -cargo install cargo-contract --force -``` - -Use the `--force` to ensure you are updated to the most recent `cargo-contract` version. - -In order to initialize a new ink! project you can use: - -``` -cargo contract new flipper -``` - -This will create a folder `flipper` in your work directory. -The folder contains a scaffold `Cargo.toml` and a `lib.rs`, which both contain the necessary building blocks for using ink!. - -The `lib.rs` contains our hello world contract ‒ the `Flipper`, which we explain in the next section. - -In order to build the contract just execute this command in the `flipper` folder: -``` -cargo +nightly contract build -``` - -As a result you'll get a file `target/flipper.wasm` file, a `metadata.json` file and a `.contract` file in the `target` folder of your contract. -The `.contract` file combines the Wasm and metadata into one file and needs to be used when instantiating the contract. - - -## Hello, World! ‒ The Flipper - -The `Flipper` contract is a simple contract containing only a single `bool` value. -It provides methods to -* flip its value from `true` to `false` (and vice versa) and -* return the current state. - - -Below you can see the code using the `ink_lang` version of ink!. - -```rust -use ink_lang as ink; - -#[ink::contract] -mod flipper { - /// The storage of the flipper contract. - #[ink(storage)] - pub struct Flipper { - /// The single `bool` value. - value: bool, - } - - impl Flipper { - /// Instantiates a new Flipper contract and initializes - /// `value` to `init_value`. - #[ink(constructor)] - pub fn new(init_value: bool) -> Self { - Self { - value: init_value, - } - } - - /// Flips `value` from `true` to `false` or vice versa. - #[ink(message)] - pub fn flip(&mut self) { - self.value = !self.value; - } - - /// Returns the current state of `value`. - #[ink(message)] - pub fn get(&self) -> bool { - self.value - } - } - - /// Simply execute `cargo test` in order to test your contract - /// using the below unit tests. - #[cfg(test)] - mod tests { - use super::*; - use ink_lang as ink; - - #[ink::test] - fn it_works() { - let mut flipper = Flipper::new(false); - assert_eq!(flipper.get(), false); - flipper.flip(); - assert_eq!(flipper.get(), true); - } - } -} -``` - -The [`flipper/src/lib.rs`](https://github.com/paritytech/ink/blob/master/examples/flipper/lib.rs) -file in our examples folder contains exactly this code. Run `cargo contract build` to build your -first ink! smart contract. - -## Examples - -In the `examples` folder you'll find a number of examples written in ink!. - -Some of the most interesting ones: - -* `delegator` ‒ Implements cross-contract calling. -* `trait-erc20` ‒ Defines a trait for `Erc20` contracts and implements it. -* `erc721` ‒ An exemplary implementation of `Erc721` NFT tokens. -* `dns` ‒ A simple `DomainNameService` smart contract. -* …and more, just rummage through the folder 🙃. - -To build a single example navigate to the root of the example and run: -``` -cargo contract build -``` - -You should now have an `.contract` file in the `target` folder of the contract. - -For information on how to upload this file to a chain, please have a look at the [Play with It](#play-with-it) section or our [smart contracts workshop](https://docs.substrate.io/tutorials/v3/ink-workshop/pt1). - - -## How it Works - -* Substrate's [Framework for Runtime Aggregation of Modularized Entities (FRAME)](https://docs.substrate.io/v3/runtime/frame) -contains a module which implements an API for typical functions smart contracts need (storage,querying information about accounts, …). -This module is called the `contracts` pallet, -* The `contracts` pallet requires smart contracts to be uploaded to the blockchain as a Wasm blob. -* ink! is a smart contract language which targets the API exposed by `contracts`. -Hence ink! contracts are compiled to Wasm. -* When executing `cargo contract build` an additional file `metadata.json` is created. -It contains information about e.g. what methods the contract provides for others to call. - -## ink! Macros & Attributes Overview - -### Entry Point - -In a module annotated with `#[ink::contract]` these attributes are available: - -| Attribute | Where Applicable | Description | -|:--|:--|:--| -| `#[ink(storage)]` | On `struct` definitions. | Defines the ink! storage struct. There can only be one ink! storage definition per contract. | -| `#[ink(message)]` | Applicable to methods. | Flags a method for the ink! storage struct as message making it available to the API for calling the contract. | -| `#[ink(constructor)]` | Applicable to method. | Flags a method for the ink! storage struct as constructor making it available to the API for instantiating the contract. | -| `#[ink(event)]` | On `struct` definitions. | Defines an ink! event. A contract can define multiple such ink! events. | -| `#[ink(anonymous)]` | Applicable to ink! events. | Tells the ink! codegen to treat the ink! event as anonymous which omits the event signature as topic upon emitting. Very similar to anonymous events in Solidity. | -| `#[ink(topic)]` | Applicable on ink! event field. | Tells the ink! codegen to provide a topic hash for the given field. Every ink! event can only have a limited number of such topic field. Similar semantics as to indexed event arguments in Solidity. | -| `#[ink(payable)]` | Applicable to ink! messages. | Allows receiving value as part of the call of the ink! message. ink! constructors are implicitly payable. | -| `#[ink(selector = S:u32)]` | Applicable to ink! messages and ink! constructors. | Specifies a concrete dispatch selector for the flagged entity. This allows a contract author to precisely control the selectors of their APIs making it possible to rename their API without breakage. | -| `#[ink(selector = _)]` | Applicable to ink! messages. | Specifies a fallback message that is invoked if no other ink! message matches a selector. | -| `#[ink(namespace = N:string)]` | Applicable to ink! trait implementation blocks. | Changes the resulting selectors of all the ink! messages and ink! constructors within the trait implementation. Allows to disambiguate between trait implementations with overlapping message or constructor names. Use only with great care and consideration! | -| `#[ink(impl)]` | Applicable to ink! implementation blocks. | Tells the ink! codegen that some implementation block shall be granted access to ink! internals even without it containing any ink! messages or ink! constructors. | - -See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a more detailed description of those and also for details on the `#[ink::contract]` macro. - -### Trait Definitions - -Use `#[ink::trait_definition]` to define your very own trait definitions that are then implementable by ink! smart contracts. -See e.g. the [`examples/trait-erc20`](https://github.com/paritytech/ink/blob/v3.0.0-rc5/examples/trait-erc20/lib.rs#L35-L37) contract on how to utilize it or [the documentation](https://paritytech.github.io/ink/ink_lang/attr.trait_definition.html) for details. - -### Off-chain Testing - -The `#[ink::test]` procedural macro enables off-chain testing. See e.g. the [`examples/erc20`](https://github.com/paritytech/ink/blob/v3.0.0-rc5/examples/erc20/lib.rs#L248-L250) contract on how to utilize those or [the documentation](https://paritytech.github.io/ink/ink_lang/attr.test.html) for details. - -## Developer Documentation - -We have [a very comprehensive documentation portal](https://ink.substrate.io), -but if you are looking for the crate level documentation itself, then these are -the relevant links: - -| Crate | Docs | Description | -|:--|:--|:--| -`ink_lang` | [![][j1]][j2] | Language features exposed by ink!. See [here](https://paritytech.github.io/ink/ink_lang/attr.contract.html) for a detailed description of attributes which you can use in an `#[ink::contract]`. | -`ink_storage` | [![][f1]][f2] | Data structures available in ink!. | -`ink_env` | [![][g1]][g2] | Low-level interface for interacting with the smart contract Wasm executor. Contains [the off-chain testing API](https://paritytech.github.io/ink/ink_env/test/index.html) as well. | -`ink_prelude` | [![][i1]][i2] | Common API for no_std and std to access alloc crate types. | - -## Community Badges - -### Normal Design - -[![Built with ink!](.images/badge.svg)](https://github.com/paritytech/ink) - -```markdown -[![Built with ink!](https://raw.githubusercontent.com/paritytech/ink/master/.images/badge.svg)](https://github.com/paritytech/ink) -``` - -### Flat Design - -[![Built with ink!](.images/badge_flat.svg)](https://github.com/paritytech/ink) - -```markdown -[![Built with ink!](https://raw.githubusercontent.com/paritytech/ink/master/.images/badge_flat.svg)](https://github.com/paritytech/ink) -``` - -## Contributing - -Visit our [contribution guidelines](CONTRIBUTING.md) for more information. - -Use the scripts provided under `scripts/check-*` directory in order to run checks on either the workspace or all examples. Please do this before pushing work in a PR. - -## License - -The entire code within this repository is licensed under the [Apache License 2.0](LICENSE). - -Please [contact us](https://www.parity.io/contact/) if you have questions about the licensing of our products. diff --git a/crates/primitives/derive/README.md b/crates/primitives/derive/README.md new file mode 120000 index 00000000000..8a33348c7d8 --- /dev/null +++ b/crates/primitives/derive/README.md @@ -0,0 +1 @@ +../../../README.md \ No newline at end of file diff --git a/crates/primitives/src/key.rs b/crates/primitives/src/key.rs index b2d8b96ac27..7c7ed6a1955 100644 --- a/crates/primitives/src/key.rs +++ b/crates/primitives/src/key.rs @@ -104,6 +104,7 @@ impl KeyComposer { } } +/// Possible errors during the computation of the storage key. #[derive(Debug, PartialEq, Eq)] pub enum Error { StructNameIsEmpty, diff --git a/crates/primitives/src/traits.rs b/crates/primitives/src/traits.rs index 513a4f5e84e..1832c77ed1a 100644 --- a/crates/primitives/src/traits.rs +++ b/crates/primitives/src/traits.rs @@ -15,6 +15,10 @@ pub use ink_primitives_derive::Storable; /// Trait for representing types which can be read and written to storage. +/// +/// This trait is not the same as the `scale::Encode + scale::Decode`. Each type that implements +/// `scale::Encode + scale::Decode` are storable by default and transferable between contracts. +/// But not each storable type is transferable. pub trait Storable: Sized { /// Convert self to a slice and append it to the destination. fn encode(&self, dest: &mut T); @@ -23,6 +27,8 @@ pub trait Storable: Sized { fn decode(input: &mut I) -> Result; } +/// Types which implement `scale::Encode` and `scale::Decode` are `Storable` by default because +/// they can be written directly into the storage cell. impl

Storable for P where P: scale::Encode + scale::Decode, diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index 35558207146..1b57a07e760 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -53,6 +53,11 @@ use scale::{ /// upgradeable contracts or you want to be resistant to future changes of storage /// key calculation strategy. /// +/// # Note +/// +/// If the contract has two or more `Lazy` with the same storage key, modifying the value of one +/// of them will modify others. +/// /// This is an example of how you can do this: /// ```rust /// # use ink_lang as ink; @@ -131,13 +136,18 @@ where V: Storable, KeyType: StorageKey, { - /// Reads the `value` from the contract storage. + /// Reads the `value` from the contract storage, if it exists. pub fn get(&self) -> Option { match ink_env::get_contract_storage::(&KeyType::KEY) { Ok(Some(value)) => Some(value), _ => None, } } + + /// Writes the given `value` to the contract storage. + pub fn set(&mut self, value: &V) { + push_storage(&KeyType::KEY, value); + } } impl Lazy @@ -147,7 +157,7 @@ where { /// Reads the `value` from the contract storage. /// - /// Returns `Default::default()` if no `value` exists. + /// Returns the default value for the storage type if no `value` exists. pub fn get_or_default(&self) -> V { match ink_env::get_contract_storage::(&KeyType::KEY) { Ok(Some(value)) => value, @@ -156,17 +166,6 @@ where } } -impl Lazy -where - V: Storable, - KeyType: StorageKey, -{ - /// Writes the given `value` to the contract storage. - pub fn set(&mut self, value: &V) { - push_storage(&KeyType::KEY, value); - } -} - impl Storable for Lazy where KeyType: StorageKey, @@ -228,7 +227,7 @@ mod tests { #[test] fn set_and_get_work() { ink_env::test::run_test::(|_| { - let mut storage: Lazy> = Lazy::new(); + let mut storage: Lazy = Lazy::new(); storage.set(&2); assert_eq!(storage.get(), Some(2)); @@ -254,7 +253,7 @@ mod tests { #[test] fn gets_or_default_if_no_key_set() { ink_env::test::run_test::(|_| { - let storage: Lazy> = Lazy::new(); + let storage: Lazy = Lazy::new(); assert_eq!(storage.get_or_default(), 0); Ok(()) @@ -263,9 +262,9 @@ mod tests { } #[test] - fn gets_fails_if_no_key_set() { + fn gets_returns_none_if_no_value_was_set() { ink_env::test::run_test::(|_| { - let storage: Lazy> = Lazy::new(); + let storage: Lazy = Lazy::new(); assert_eq!(storage.get(), None); Ok(()) diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 358f6a1a63a..d6f960c0e57 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -57,5 +57,6 @@ pub use self::lazy::{ Mapping, }; -#[doc(inline)] -pub use self::traits::pull_or_init; +#[macro_use] +#[doc(hidden)] +pub mod pull_or_init; diff --git a/crates/storage/src/traits/pull_or_init.rs b/crates/storage/src/pull_or_init.rs similarity index 83% rename from crates/storage/src/traits/pull_or_init.rs rename to crates/storage/src/pull_or_init.rs index 7f7f4f75cdd..e4d7a47153f 100644 --- a/crates/storage/src/traits/pull_or_init.rs +++ b/crates/storage/src/pull_or_init.rs @@ -13,12 +13,16 @@ // limitations under the License. //! The module helps during compilation time decide which pull mechanism to use. -//! If the type implements `OnCallInitializer` trait, it will use `pull_storage` and -//! initialization(if the pull failed). Otherwise, it will use only `pull_storage`. +//! If the type implements [`OnCallInitializer`](crate::traits::storage::OnCallInitializer) trait, +//! it will use `pull_storage` with combination `OnCallInitializer::initialize`(if the pull failed). +//! Otherwise, it will use only `pull_storage` as a default behavior. +//! +//! [`OnCallInitializer`](crate::traits::storage::OnCallInitializer) allows initialize the +//! type on demand. For more information, check the documentation of the trait. use crate::traits::{ pull_storage, - storage::OnCallInitializer, + OnCallInitializer, }; use ink_primitives::{ traits::Storable, @@ -58,9 +62,9 @@ impl PullOrInitFallback for PullOrInit {} macro_rules! pull_or_init { ( $T:ty, $key:expr $(,)? ) => {{ #[allow(unused_imports)] - use $crate::traits::pull_or_init::PullOrInitFallback as _; + use $crate::pull_or_init::PullOrInitFallback as _; - $crate::traits::pull_or_init::PullOrInit::<$T>::pull_or_init(&$key) + $crate::pull_or_init::PullOrInit::<$T>::pull_or_init(&$key) }}; } diff --git a/crates/storage/src/traits/impls/mod.rs b/crates/storage/src/traits/impls/mod.rs index 65236bb7814..8c339ddf67e 100644 --- a/crates/storage/src/traits/impls/mod.rs +++ b/crates/storage/src/traits/impls/mod.rs @@ -38,7 +38,9 @@ impl StorageKey for AutoKey { impl Debug for AutoKey { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("AutoKey").finish() + f.debug_struct("AutoKey") + .field("key", &::KEY) + .finish() } } @@ -107,4 +109,52 @@ where } #[cfg(test)] -mod tests; +mod tests { + mod arrays { + use crate::item_works_for_primitive; + + type Array = [i32; 4]; + item_works_for_primitive!(Array); + + type ArrayTuples = [(i32, i32); 2]; + item_works_for_primitive!(ArrayTuples); + } + + mod prims { + use crate::item_works_for_primitive; + use ink_env::AccountId; + + 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; + item_works_for_primitive!(OptionU8); + + type ResultU8 = Result; + item_works_for_primitive!(ResultU8); + + type BoxU8 = Box; + item_works_for_primitive!(BoxU8); + + type BoxOptionU8 = Box>; + item_works_for_primitive!(BoxOptionU8); + } + + mod tuples { + use crate::item_works_for_primitive; + + type TupleSix = (i32, u32, String, u8, bool, Box>); + item_works_for_primitive!(TupleSix); + } +} diff --git a/crates/storage/src/traits/impls/tests.rs b/crates/storage/src/traits/impls/tests.rs deleted file mode 100644 index d05f2c8b8f3..00000000000 --- a/crates/storage/src/traits/impls/tests.rs +++ /dev/null @@ -1,47 +0,0 @@ -mod arrays { - use crate::item_works_for_primitive; - - type Array = [i32; 4]; - item_works_for_primitive!(Array); - - type ArrayTuples = [(i32, i32); 2]; - item_works_for_primitive!(ArrayTuples); -} - -mod prims { - use crate::item_works_for_primitive; - use ink_env::AccountId; - - 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; - item_works_for_primitive!(OptionU8); - - type ResultU8 = Result; - item_works_for_primitive!(ResultU8); - - type BoxU8 = Box; - item_works_for_primitive!(BoxU8); - - type BoxOptionU8 = Box>; - item_works_for_primitive!(BoxOptionU8); -} - -mod tuples { - use crate::item_works_for_primitive; - - type TupleSix = (i32, u32, String, u8, bool, Box>); - item_works_for_primitive!(TupleSix); -} diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index 6c1fcd02683..f8d206f86ad 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -19,10 +19,11 @@ //! to tell a smart contract how to load and store instances of this type //! from and to the contract's storage. //! -//! The `Packed` shows that the type is packed and can be stored -//! into single storage cell. Some collections works only with packed structures. -//! Consequently, non-`Packed` are types that can't be stored in one cell. -//! It means that at least one of the fields has its storage cell. +//! The `Packed` shows that the type can be stored into single storage cell. +//! In most cases, collections(`Vec`, `HashMap`, `HashSet` etc.) work only with packed structures. +//! +//! If at least one of the type's fields occupies its own separate storage cell, it is a +//! non-`Packed` type because it occupies more than one storage cell. mod impls; mod storage; @@ -30,10 +31,6 @@ mod storage; #[cfg(feature = "std")] mod layout; -#[macro_use] -#[doc(hidden)] -pub mod pull_or_init; - #[cfg(feature = "std")] pub use self::layout::{ LayoutCryptoHasher, @@ -63,7 +60,12 @@ pub use ink_storage_derive::{ StorageLayout, }; -/// Pulls an instance of type `T` from the contract storage using decode and its storage key. +/// Pulls an instance of type `T` from the contract storage given its storage key. +/// +/// # Panics +/// +/// - If the storage entry is empty. +/// - If could not properly decode storage entry. pub fn pull_storage(key: &Key) -> T where T: Storable, @@ -75,7 +77,8 @@ where } } -/// Pushes the entity to the contract storage using encode and storage key. +/// Pushes the entity to the contract storage at the provided storage key and returns the size +/// of pre-existing value if any. pub fn push_storage(key: &Key, entity: &T) -> Option where T: Storable, From 1f7603f116cfb55cfa664e93caf8cb98072fcc81 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Tue, 16 Aug 2022 10:05:41 +0100 Subject: [PATCH 15/40] Fix doc --- crates/storage/src/pull_or_init.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/storage/src/pull_or_init.rs b/crates/storage/src/pull_or_init.rs index e4d7a47153f..5118f9d049b 100644 --- a/crates/storage/src/pull_or_init.rs +++ b/crates/storage/src/pull_or_init.rs @@ -13,11 +13,11 @@ // limitations under the License. //! The module helps during compilation time decide which pull mechanism to use. -//! If the type implements [`OnCallInitializer`](crate::traits::storage::OnCallInitializer) trait, +//! If the type implements [`OnCallInitializer`](crate::traits::OnCallInitializer) trait, //! it will use `pull_storage` with combination `OnCallInitializer::initialize`(if the pull failed). //! Otherwise, it will use only `pull_storage` as a default behavior. //! -//! [`OnCallInitializer`](crate::traits::storage::OnCallInitializer) allows initialize the +//! [`OnCallInitializer`](crate::traits::OnCallInitializer) allows initialize the //! type on demand. For more information, check the documentation of the trait. use crate::traits::{ From 07a71ea767dac542f4f622249119f285700ed579 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Tue, 16 Aug 2022 18:23:44 +0100 Subject: [PATCH 16/40] Add comment to autoref specialisation --- crates/storage/src/pull_or_init.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/storage/src/pull_or_init.rs b/crates/storage/src/pull_or_init.rs index 5118f9d049b..754c45f2bf6 100644 --- a/crates/storage/src/pull_or_init.rs +++ b/crates/storage/src/pull_or_init.rs @@ -29,6 +29,8 @@ use ink_primitives::{ Key, }; +/// Part of Autoref-Based Specialization. It is a wrapper around the type to support autoref +/// specialization. pub struct PullOrInit { marker: core::marker::PhantomData T>, } @@ -48,6 +50,8 @@ impl PullOrInit { } } +/// Part of Autoref-Based Specialization. If the type doesn't implement `OnCallInitializer` trait +/// then the compiler will use this default implementation. pub trait PullOrInitFallback { #[allow(dead_code)] fn pull_or_init(key: &Key) -> T { From 07889afb61e602167c57971aed2bc28029d5e730 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Aug 2022 11:25:32 +0100 Subject: [PATCH 17/40] Suggestion from the review --- crates/primitives/derive/src/storable.rs | 4 ++-- crates/storage/codegen/src/lib.rs | 4 +++- crates/storage/derive/src/item.rs | 4 ++-- crates/storage/derive/src/lib.rs | 23 +++++++++-------------- crates/storage/src/lazy/mapping.rs | 15 +++++++++++++++ 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/crates/primitives/derive/src/storable.rs b/crates/primitives/derive/src/storable.rs index dc26621e40a..e5ea940c96f 100644 --- a/crates/primitives/derive/src/storable.rs +++ b/crates/primitives/derive/src/storable.rs @@ -74,7 +74,7 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { }) .enumerate() .fold(quote! {}, |acc, (index, variant)| { - let index = index as u8; + let index = index as usize; quote! { #acc #index => #variant, @@ -83,7 +83,7 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { let encode_body = s.variants().iter().enumerate().map(|(index, variant)| { let pat = variant.pat(); - let index = index as u8; + let index = index as usize; let fields = variant.bindings().iter().map(|field| { let span = field.ast().ty.span(); quote_spanned!(span => diff --git a/crates/storage/codegen/src/lib.rs b/crates/storage/codegen/src/lib.rs index 5ab54072f8c..abda7cd1bf0 100644 --- a/crates/storage/codegen/src/lib.rs +++ b/crates/storage/codegen/src/lib.rs @@ -17,7 +17,9 @@ use syn::Data; /// Provides common methods for `DeriveInput`. /// -/// **Note:** This is only for internal usage in the `codegen` module. +/// # Developer 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 `StorageKey`. diff --git a/crates/storage/derive/src/item.rs b/crates/storage/derive/src/item.rs index a870d5b8e96..15cb3610349 100644 --- a/crates/storage/derive/src/item.rs +++ b/crates/storage/derive/src/item.rs @@ -36,8 +36,8 @@ fn item_inner(s: synstructure::Structure) -> TokenStream2 { 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(); + if let Some(inner_salt_ident) = s.ast().find_salt() { + let inner_salt_ident = inner_salt_ident.ident.to_token_stream(); let ty_generics: Vec<_> = s .ast() .generics diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index 18a59e5864f..a73d8d45109 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -33,7 +33,11 @@ use self::{ }; synstructure::decl_derive!( [Item] => - /// Derives `ink_storage`'s `Item` trait for the given `struct` or `enum`. + /// Derives `ink_storage`'s [`Item`](ink_storage::traits::Item) trait for the given `struct` + /// or `enum`. + /// + /// If the type declaration contains generic [`StorageKey`](ink_storage::traits::StorageKey), + /// it will use it as salt to generate a combined storage key. /// /// # Examples /// @@ -55,23 +59,13 @@ synstructure::decl_derive!( /// /// let _: NamedFields = >::Type::default(); /// let _: NamedFields = >>::Type::default(); - /// - /// #[derive(Item, StorageKey, Storable)] - /// struct NamedFieldsStorage { - /// a: >>::Type, - /// b: <[u32; 32] as AutoItem>>::Type, - /// } - /// - /// // (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); /// ``` item_derive ); synstructure::decl_derive!( [StorageKey] => - /// Derives `ink_storage`'s `StorageKey` trait for the given `struct` or `enum`. + /// Derives `ink_storage`'s [`StorageKey`](ink_storage::traits::StorageKey) trait for the given + /// `struct` or `enum`. /// /// # Examples /// @@ -105,7 +99,8 @@ synstructure::decl_derive!( ); synstructure::decl_derive!( [StorageLayout] => - /// Derives `ink_storage`'s `StorageLayout` trait for the given `struct` or `enum`. + /// Derives `ink_storage`'s [`StorageKey`](ink_storage::traits::StorageLayout) trait for the + /// given `struct` or `enum`. /// /// # Examples /// diff --git a/crates/storage/src/lazy/mapping.rs b/crates/storage/src/lazy/mapping.rs index 15f7bb891b3..2a28165bcae 100644 --- a/crates/storage/src/lazy/mapping.rs +++ b/crates/storage/src/lazy/mapping.rs @@ -258,6 +258,7 @@ const _: () = { #[cfg(test)] mod tests { use super::*; + use crate::traits::ManualKey; #[test] fn insert_and_get_work() { @@ -271,6 +272,20 @@ mod tests { .unwrap() } + #[test] + fn insert_and_get_work_for_two_mapping_with_same_manual_key() { + ink_env::test::run_test::(|_| { + let mut mapping: Mapping> = Mapping::new(); + mapping.insert(&1, &2); + + let mapping2: Mapping> = Mapping::new(); + assert_eq!(mapping2.get(&1), Some(2)); + + Ok(()) + }) + .unwrap() + } + #[test] fn gets_default_if_no_key_set() { ink_env::test::run_test::(|_| { From f64d7f763554f4c536a56347240d3e4f8b44ae64 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Aug 2022 13:30:03 +0100 Subject: [PATCH 18/40] Revert back u8 --- crates/primitives/derive/src/storable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/primitives/derive/src/storable.rs b/crates/primitives/derive/src/storable.rs index e5ea940c96f..dc26621e40a 100644 --- a/crates/primitives/derive/src/storable.rs +++ b/crates/primitives/derive/src/storable.rs @@ -74,7 +74,7 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { }) .enumerate() .fold(quote! {}, |acc, (index, variant)| { - let index = index as usize; + let index = index as u8; quote! { #acc #index => #variant, @@ -83,7 +83,7 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { let encode_body = s.variants().iter().enumerate().map(|(index, variant)| { let pat = variant.pat(); - let index = index as usize; + let index = index as u8; let fields = variant.bindings().iter().map(|field| { let span = field.ast().ty.span(); quote_spanned!(span => From efff8bf8638537cecc4a4a9a11efb36ca6440227 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Aug 2022 20:27:46 +0100 Subject: [PATCH 19/40] Remove unwrap --- crates/storage/codegen/src/lib.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/storage/codegen/src/lib.rs b/crates/storage/codegen/src/lib.rs index abda7cd1bf0..62de8740abe 100644 --- a/crates/storage/codegen/src/lib.rs +++ b/crates/storage/codegen/src/lib.rs @@ -34,14 +34,13 @@ 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 Some(bound) = type_param.bounds.first() { if let syn::TypeParamBound::Trait(trait_bound) = bound { let segments = &trait_bound.path.segments; - if !segments.is_empty() - && segments.last().unwrap().ident == "StorageKey" - { - return Some(type_param.clone()) + if let Some(last) = segments.last() { + if last.ident == "StorageKey" { + return Some(type_param.clone()) + } } } } From 556ce206ea680954ca108f24b8a436b01bf6c1f7 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Aug 2022 20:33:10 +0100 Subject: [PATCH 20/40] Collapse if let --- crates/storage/codegen/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/storage/codegen/src/lib.rs b/crates/storage/codegen/src/lib.rs index 62de8740abe..65de0226a05 100644 --- a/crates/storage/codegen/src/lib.rs +++ b/crates/storage/codegen/src/lib.rs @@ -34,13 +34,13 @@ 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 let Some(bound) = type_param.bounds.first() { - if let syn::TypeParamBound::Trait(trait_bound) = bound { - let segments = &trait_bound.path.segments; - if let Some(last) = segments.last() { - if last.ident == "StorageKey" { - return Some(type_param.clone()) - } + if let Some(syn::TypeParamBound::Trait(trait_bound)) = + type_param.bounds.first() + { + let segments = &trait_bound.path.segments; + if let Some(last) = segments.last() { + if last.ident == "StorageKey" { + return Some(type_param.clone()) } } } From 5ce970d41be41f52dabc8629252cf21cee8a027f Mon Sep 17 00:00:00 2001 From: xgreenx Date: Wed, 17 Aug 2022 22:54:46 +0100 Subject: [PATCH 21/40] Fixed overflow for enums --- crates/primitives/derive/src/storable.rs | 14 ++++++++++++-- crates/storage/derive/src/storage_layout.rs | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/crates/primitives/derive/src/storable.rs b/crates/primitives/derive/src/storable.rs index dc26621e40a..1ddbfdf8eb0 100644 --- a/crates/primitives/derive/src/storable.rs +++ b/crates/primitives/derive/src/storable.rs @@ -130,9 +130,19 @@ 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 { + match &s.ast().data { syn::Data::Struct(_) => storable_struct_derive(&s), - syn::Data::Enum(_) => storable_enum_derive(&s), + syn::Data::Enum(data) => { + if s.variants().len() > 256 { + return syn::Error::new( + data.variants.span(), + "Currently only enums with at most 256 variants are supported.", + ) + .to_compile_error() + } + + 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 5283b29d8f6..fcc3ea376f4 100644 --- a/crates/storage/derive/src/storage_layout.rs +++ b/crates/storage/derive/src/storage_layout.rs @@ -14,6 +14,7 @@ use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use syn::spanned::Spanned; fn field_layout<'a>( variant: &'a synstructure::VariantInfo, @@ -117,9 +118,19 @@ pub fn storage_layout_derive(mut s: synstructure::Structure) -> TokenStream2 { s.bind_with(|_| synstructure::BindStyle::Move) .add_bounds(synstructure::AddBounds::Fields) .underscore_const(true); - match s.ast().data { + match &s.ast().data { syn::Data::Struct(_) => storage_layout_struct(&s), - syn::Data::Enum(_) => storage_layout_enum(&s), + syn::Data::Enum(data) => { + if s.variants().len() > 256 { + return syn::Error::new( + data.variants.span(), + "Currently only enums with at most 256 variants are supported.", + ) + .to_compile_error() + } + + storage_layout_enum(&s) + } _ => panic!("cannot derive `StorageLayout` for Rust `union` items"), } } From 8fcdab2651f789445324a2920d4742f932b27b0d Mon Sep 17 00:00:00 2001 From: xgreenx Date: Thu, 18 Aug 2022 19:47:58 +0100 Subject: [PATCH 22/40] Fixing comments --- crates/engine/src/ext.rs | 1 + crates/env/src/api.rs | 2 ++ crates/env/src/backend.rs | 2 ++ crates/storage/derive/src/lib.rs | 2 +- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/ext.rs b/crates/engine/src/ext.rs index e5ae5f488fa..94ff7840d26 100644 --- a/crates/engine/src/ext.rs +++ b/crates/engine/src/ext.rs @@ -269,6 +269,7 @@ impl Engine { } /// Removes the storage entries at the given key. + /// Returns the size of the previously stored value at the key if any. pub fn clear_storage(&mut self, key: &[u8]) -> Option { let callee = self.get_callee(); let account_id = AccountId::from_bytes(&callee[..]); diff --git a/crates/env/src/api.rs b/crates/env/src/api.rs index 1dc25fb38a7..d627bdbef23 100644 --- a/crates/env/src/api.rs +++ b/crates/env/src/api.rs @@ -227,6 +227,8 @@ where } /// Clears the contract's storage entry under the given storage key. +/// +/// If a value was stored under the specified storage key, the size of the value is returned. pub fn clear_contract_storage(key: &K) -> Option where K: scale::Encode, diff --git a/crates/env/src/backend.rs b/crates/env/src/backend.rs index 755e2562d2c..30313bb7879 100644 --- a/crates/env/src/backend.rs +++ b/crates/env/src/backend.rs @@ -186,6 +186,8 @@ pub trait EnvBackend { K: scale::Encode; /// Clears the contract's storage key entry under the given storage key. + /// + /// Returns the size of the previously stored value at the specified key if any. fn clear_contract_storage(&mut self, key: &K) -> Option where K: scale::Encode; diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index a73d8d45109..98f7a9ab8fc 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -99,7 +99,7 @@ synstructure::decl_derive!( ); synstructure::decl_derive!( [StorageLayout] => - /// Derives `ink_storage`'s [`StorageKey`](ink_storage::traits::StorageLayout) trait for the + /// Derives `ink_storage`'s [`StorageLayout`](ink_storage::traits::StorageLayout) trait for the /// given `struct` or `enum`. /// /// # Examples From ca9f95b1398186991cc36c2715817433e85c2afc Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 19 Aug 2022 16:42:33 +0100 Subject: [PATCH 23/40] Renamed `Item` to `StorableHint` and `AutoItem` to `AutoStorableHint` --- .../codegen/src/generator/storage_item.rs | 4 +- crates/lang/macro/src/lib.rs | 12 ++-- .../fail/collections_only_packed_1.stderr | 24 +++---- .../fail/collections_only_packed_2.stderr | 12 ++-- crates/storage/derive/src/lib.rs | 26 ++++---- .../derive/src/{item.rs => storable_hint.rs} | 10 +-- crates/storage/derive/src/tests/mod.rs | 2 +- .../src/tests/{item.rs => storable_hint.rs} | 26 ++++---- crates/storage/src/lazy/mapping.rs | 4 +- crates/storage/src/lazy/mod.rs | 6 +- crates/storage/src/test_utils.rs | 6 +- crates/storage/src/traits/impls/mod.rs | 66 +++++++++---------- crates/storage/src/traits/mod.rs | 6 +- crates/storage/src/traits/storage.rs | 4 +- 14 files changed, 104 insertions(+), 104 deletions(-) rename crates/storage/derive/src/{item.rs => storable_hint.rs} (84%) rename crates/storage/derive/src/tests/{item.rs => storable_hint.rs} (83%) diff --git a/crates/lang/codegen/src/generator/storage_item.rs b/crates/lang/codegen/src/generator/storage_item.rs index fe778ad7e60..c0c8b8d7480 100644 --- a/crates/lang/codegen/src/generator/storage_item.rs +++ b/crates/lang/codegen/src/generator/storage_item.rs @@ -61,7 +61,7 @@ impl GenerateCode for StorageItem<'_> { ::ink_storage::traits::StorageLayout, ))] #[derive( - ::ink_storage::traits::Item, + ::ink_storage::traits::StorableHint, ::ink_storage::traits::StorageKey, ::ink_primitives::traits::Storable, )] @@ -240,7 +240,7 @@ fn convert_into_storage_field( 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< + <#ty as ::ink_storage::traits::AutoStorableHint< ::ink_storage::traits::ManualKey<#key, #salt>, >>::Type )); diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index 896762fe469..ba8dfb9b132 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -698,7 +698,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// }; /// use ink_storage::traits::{ /// StorageKey, -/// Item, +/// StorableHint, /// }; /// use ink_primitives::traits::Storable; /// @@ -706,7 +706,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// struct Packed { /// s1: u128, /// s2: Vec, -/// // Fails because `Item` is only implemented for `Vec` where `T: Packed`. +/// // Fails because `StorableHint` is only implemented for `Vec` where `T: Packed`. /// // s3: Vec, /// } /// @@ -718,7 +718,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// struct PackedManual { /// s1: u32, /// s2: Vec<(u128, String)>, -/// // Fails because `Item` is only implemented for `Vec` where `T: Packed`. +/// // Fails because `StorableHint` is only implemented for `Vec` where `T: Packed`. /// // s3: Vec, /// } /// @@ -734,7 +734,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// } /// /// #[ink_lang::storage_item(derive = false)] -/// #[derive(Storable, Item, StorageKey)] +/// #[derive(Storable, StorableHint, StorageKey)] /// #[cfg_attr( /// feature = "std", /// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) @@ -790,13 +790,13 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// ``` /// use ink_storage::Mapping; /// use ink_storage::traits::{ -/// Item, +/// StorableHint, /// StorageKey, /// }; /// use ink_primitives::traits::Storable; /// /// #[ink_lang::storage_item(derive = false)] -/// #[derive(Item, Storable, StorageKey)] +/// #[derive(StorableHint, Storable, StorageKey)] /// struct NonPackedGeneric { /// s1: u32, /// s2: Mapping, 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 index bc77a1650c3..5e5a42996e8 100644 --- 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 @@ -6,8 +6,8 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied | = 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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied --> tests/ui/storage_item/fail/collections_only_packed_1.rs:11:8 @@ -20,8 +20,8 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied [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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` error[E0277]: the trait bound `Vec: Decode` is not satisfied --> tests/ui/storage_item/fail/collections_only_packed_1.rs:9:1 @@ -31,8 +31,8 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied | = 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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 | @@ -56,8 +56,8 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied [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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 | @@ -78,8 +78,8 @@ error[E0277]: the trait bound `Vec: Decode` is not satisfied | = 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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 | @@ -103,8 +103,8 @@ error[E0277]: the trait bound `[NonPacked]: Encode` is not satisfied [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 of the requirements on the impl of `StorableHint<()>` for `Vec` + = note: required because of the requirements on the impl of `AutoStorableHint>` for `Vec` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_1.rs:10:8 | 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 index ca4ecbe9b2a..d7da1c08c88 100644 --- 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 @@ -7,7 +7,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied --> tests/ui/storage_item/fail/collections_only_packed_2.rs:11:8 @@ -18,7 +18,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` error[E0277]: the trait bound `BTreeMap: Decode` is not satisfied --> tests/ui/storage_item/fail/collections_only_packed_2.rs:9:1 @@ -29,7 +29,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 | @@ -51,7 +51,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 | @@ -73,7 +73,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 | @@ -95,7 +95,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi = 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 of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 | diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index 98f7a9ab8fc..eb2af72c2b3 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -19,21 +19,21 @@ extern crate proc_macro; -mod item; mod key_holder; +mod storable_hint; mod storage_layout; #[cfg(test)] mod tests; use self::{ - item::item_derive, key_holder::key_holder_derive, + storable_hint::storable_hint_derive, storage_layout::storage_layout_derive, }; synstructure::decl_derive!( - [Item] => - /// Derives `ink_storage`'s [`Item`](ink_storage::traits::Item) trait for the given `struct` + [StorableHint] => + /// Derives `ink_storage`'s [`StorableHint`](ink_storage::traits::StorableHint) trait for the given `struct` /// or `enum`. /// /// If the type declaration contains generic [`StorageKey`](ink_storage::traits::StorageKey), @@ -43,24 +43,24 @@ synstructure::decl_derive!( /// /// ``` /// use ink_storage::traits::{ - /// Item, + /// StorableHint, /// StorageKey, - /// AutoItem, + /// AutoStorableHint, /// AutoKey, /// ManualKey, /// }; /// use ink_primitives::traits::Storable; /// - /// #[derive(Default, Item, Storable)] + /// #[derive(Default, StorableHint, Storable)] /// struct NamedFields { /// a: u32, /// b: [u32; 32], /// } /// - /// let _: NamedFields = >::Type::default(); - /// let _: NamedFields = >>::Type::default(); + /// let _: NamedFields = >::Type::default(); + /// let _: NamedFields = >>::Type::default(); /// ``` - item_derive + storable_hint_derive ); synstructure::decl_derive!( [StorageKey] => @@ -71,7 +71,7 @@ synstructure::decl_derive!( /// /// ``` /// use ink_storage::traits::{ - /// AutoItem, + /// AutoStorableHint, /// StorageKey, /// ManualKey, /// AutoKey, @@ -87,8 +87,8 @@ synstructure::decl_derive!( /// /// #[derive(StorageKey)] /// struct NamedFieldsManualKey { - /// a: >>::Type, - /// b: <[u32; 32] as AutoItem>>::Type, + /// a: >>::Type, + /// b: <[u32; 32] as AutoStorableHint>>::Type, /// } /// /// assert_eq!( as StorageKey>::KEY, 0); diff --git a/crates/storage/derive/src/item.rs b/crates/storage/derive/src/storable_hint.rs similarity index 84% rename from crates/storage/derive/src/item.rs rename to crates/storage/derive/src/storable_hint.rs index 15cb3610349..66053e7d4c9 100644 --- a/crates/storage/derive/src/item.rs +++ b/crates/storage/derive/src/storable_hint.rs @@ -24,7 +24,7 @@ use syn::{ GenericParam, }; -fn item_inner(s: synstructure::Structure) -> TokenStream2 { +fn storable_hint_inner(s: synstructure::Structure) -> TokenStream2 { let ident = s.ast().ident.clone(); let salt_ident = format_ident!("__ink_generic_salt"); @@ -61,14 +61,14 @@ fn item_inner(s: synstructure::Structure) -> TokenStream2 { .collect(); quote! { - impl #impl_generics ::ink_storage::traits::Item<#salt_ident> for #ident #ty_generics_original #where_clause { + impl #impl_generics ::ink_storage::traits::StorableHint<#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 { + impl #impl_generics ::ink_storage::traits::StorableHint<#salt_ident> for #ident #ty_generics_original #where_clause { type Type = #ident #ty_generics_original; type PreferredKey = ::ink_storage::traits::AutoKey; } @@ -76,8 +76,8 @@ fn item_inner(s: synstructure::Structure) -> TokenStream2 { } } -pub fn item_derive(s: synstructure::Structure) -> TokenStream2 { - let derive = item_inner(s); +pub fn storable_hint_derive(s: synstructure::Structure) -> TokenStream2 { + let derive = storable_hint_inner(s); quote! { const _ : () = { diff --git a/crates/storage/derive/src/tests/mod.rs b/crates/storage/derive/src/tests/mod.rs index f3db5831ea5..4f605461045 100644 --- a/crates/storage/derive/src/tests/mod.rs +++ b/crates/storage/derive/src/tests/mod.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod item; mod key_holder; +mod storable_hint; mod storage_layout; #[macro_export] diff --git a/crates/storage/derive/src/tests/item.rs b/crates/storage/derive/src/tests/storable_hint.rs similarity index 83% rename from crates/storage/derive/src/tests/item.rs rename to crates/storage/derive/src/tests/storable_hint.rs index b30e336e256..288dee8e9ac 100644 --- a/crates/storage/derive/src/tests/item.rs +++ b/crates/storage/derive/src/tests/storable_hint.rs @@ -12,18 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::item_derive; +use crate::storable_hint_derive; #[test] fn unit_struct_works() { crate::test_derive! { - item_derive { + storable_hint_derive { struct UnitStruct; } expands to { const _: () = { impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> - ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct + ::ink_storage::traits::StorableHint<__ink_generic_salt> for UnitStruct { type Type = UnitStruct; type PreferredKey = ::ink_storage::traits::AutoKey; @@ -37,7 +37,7 @@ fn unit_struct_works() { #[test] fn unit_struct_salt_works() { crate::test_derive! { - item_derive { + storable_hint_derive { struct UnitStruct; } expands to { @@ -46,7 +46,7 @@ fn unit_struct_salt_works() { Salt: ::ink_storage::traits::StorageKey, __ink_generic_salt: ::ink_storage::traits::StorageKey > - ::ink_storage::traits::Item<__ink_generic_salt> for UnitStruct + ::ink_storage::traits::StorableHint<__ink_generic_salt> for UnitStruct { type Type = UnitStruct<__ink_generic_salt>; type PreferredKey = Salt; @@ -60,7 +60,7 @@ fn unit_struct_salt_works() { #[test] fn struct_works() { crate::test_derive! { - item_derive { + storable_hint_derive { struct NamedFields { a: i32, b: [u8; 32], @@ -70,7 +70,7 @@ fn struct_works() { expands to { const _: () = { impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> - ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields + ::ink_storage::traits::StorableHint<__ink_generic_salt> for NamedFields { type Type = NamedFields; type PreferredKey = ::ink_storage::traits::AutoKey; @@ -84,7 +84,7 @@ fn struct_works() { #[test] fn struct_salt_works() { crate::test_derive! { - item_derive { + storable_hint_derive { struct NamedFields { a: i32, b: [u8; 32], @@ -97,7 +97,7 @@ fn struct_salt_works() { Salt: StorageKey, __ink_generic_salt: ::ink_storage::traits::StorageKey > - ::ink_storage::traits::Item<__ink_generic_salt> for NamedFields + ::ink_storage::traits::StorableHint<__ink_generic_salt> for NamedFields { type Type = NamedFields<__ink_generic_salt>; type PreferredKey = Salt; @@ -111,7 +111,7 @@ fn struct_salt_works() { #[test] fn enum_works() { crate::test_derive! { - item_derive { + storable_hint_derive { enum MixedEnum { A, B(i32, [u8; 32]), @@ -121,7 +121,7 @@ fn enum_works() { expands to { const _: () = { impl<__ink_generic_salt: ::ink_storage::traits::StorageKey> - ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum + ::ink_storage::traits::StorableHint<__ink_generic_salt> for MixedEnum { type Type = MixedEnum; type PreferredKey = ::ink_storage::traits::AutoKey; @@ -135,7 +135,7 @@ fn enum_works() { #[test] fn enum_salt_works() { crate::test_derive! { - item_derive { + storable_hint_derive { enum MixedEnum { A, B(u32, [u8; 32]), @@ -148,7 +148,7 @@ fn enum_salt_works() { Salt: traits::StorageKey, __ink_generic_salt: ::ink_storage::traits::StorageKey > - ::ink_storage::traits::Item<__ink_generic_salt> for MixedEnum + ::ink_storage::traits::StorableHint<__ink_generic_salt> for MixedEnum { type Type = MixedEnum<__ink_generic_salt>; type PreferredKey = Salt; diff --git a/crates/storage/src/lazy/mapping.rs b/crates/storage/src/lazy/mapping.rs index 2a28165bcae..4a933866264 100644 --- a/crates/storage/src/lazy/mapping.rs +++ b/crates/storage/src/lazy/mapping.rs @@ -21,8 +21,8 @@ use crate::traits::{ AutoKey, - Item, Packed, + StorableHint, StorageKey, }; use core::marker::PhantomData; @@ -213,7 +213,7 @@ where } } -impl Item for Mapping +impl StorableHint for Mapping where V: Packed, Key: StorageKey, diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index 1b57a07e760..1cc27f5bbbf 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -26,7 +26,7 @@ pub use self::mapping::Mapping; use crate::traits::{ push_storage, AutoKey, - Item, + StorableHint, StorageKey, }; use core::marker::PhantomData; @@ -179,11 +179,11 @@ where } } -impl Item for Lazy +impl StorableHint for Lazy where Key: StorageKey, InnerKey: StorageKey, - V: Item, + V: StorableHint, { type Type = Lazy; type PreferredKey = InnerKey; diff --git a/crates/storage/src/test_utils.rs b/crates/storage/src/test_utils.rs index 1ce70230ef0..f58b6c697cf 100644 --- a/crates/storage/src/test_utils.rs +++ b/crates/storage/src/test_utils.rs @@ -30,16 +30,16 @@ where /// Creates test to verify that the primitive types are packed. #[macro_export] -macro_rules! item_works_for_primitive { +macro_rules! storage_hint_works_for_primitive { ( $ty:ty ) => { paste::item! { #[test] #[allow(non_snake_case)] - fn [<$ty _item_works>] () { + fn [<$ty _storage_hint_works>] () { $crate::test_utils::run_test(|| { assert_eq!( ::core::any::TypeId::of::<$ty>(), - ::core::any::TypeId::of::<<$ty as $crate::traits::Item<$crate::traits::ManualKey<123>>>::Type>() + ::core::any::TypeId::of::<<$ty as $crate::traits::StorableHint<$crate::traits::ManualKey<123>>>::Type>() ); }) } diff --git a/crates/storage/src/traits/impls/mod.rs b/crates/storage/src/traits/impls/mod.rs index 8c339ddf67e..101e0212c75 100644 --- a/crates/storage/src/traits/impls/mod.rs +++ b/crates/storage/src/traits/impls/mod.rs @@ -13,9 +13,9 @@ // limitations under the License. use crate::traits::{ - AutoItem, - Item, + AutoStorableHint, Packed, + StorableHint, StorageKey, }; use core::{ @@ -75,19 +75,19 @@ impl StorageKey for ResolverKey { } type FinalKey = - ResolverKey<>::PreferredKey, ManualKey>; + ResolverKey<>::PreferredKey, ManualKey>; -// `AutoItem` trait figures out that storage key it should use. +// `AutoStorableHint` trait figures out that storage key it should use. // - If the `PreferredKey` is `AutoKey` it will use an auto-generated key passed as generic -// into `AutoItem`. +// into `AutoStorableHint`. // - If `PreferredKey` is `ManualKey`, then it will use it. -impl AutoItem> for T +impl AutoStorableHint> for T where - T: Item, - T: Item>, + T: StorableHint, + T: StorableHint>, ParentKey: StorageKey, { - type Type = >>::Type; + type Type = >>::Type; } impl

Packed for P where P: scale::Decode + scale::Encode {} @@ -99,7 +99,7 @@ where const KEY: Key = 0; } -impl Item for P +impl StorableHint for P where P: Packed, Key: StorageKey, @@ -111,50 +111,50 @@ where #[cfg(test)] mod tests { mod arrays { - use crate::item_works_for_primitive; + use crate::storage_hint_works_for_primitive; type Array = [i32; 4]; - item_works_for_primitive!(Array); + storage_hint_works_for_primitive!(Array); type ArrayTuples = [(i32, i32); 2]; - item_works_for_primitive!(ArrayTuples); + storage_hint_works_for_primitive!(ArrayTuples); } mod prims { - use crate::item_works_for_primitive; + use crate::storage_hint_works_for_primitive; use ink_env::AccountId; - 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); + storage_hint_works_for_primitive!(bool); + storage_hint_works_for_primitive!(String); + storage_hint_works_for_primitive!(AccountId); + storage_hint_works_for_primitive!(i8); + storage_hint_works_for_primitive!(i16); + storage_hint_works_for_primitive!(i32); + storage_hint_works_for_primitive!(i64); + storage_hint_works_for_primitive!(i128); + storage_hint_works_for_primitive!(u8); + storage_hint_works_for_primitive!(u16); + storage_hint_works_for_primitive!(u32); + storage_hint_works_for_primitive!(u64); + storage_hint_works_for_primitive!(u128); type OptionU8 = Option; - item_works_for_primitive!(OptionU8); + storage_hint_works_for_primitive!(OptionU8); type ResultU8 = Result; - item_works_for_primitive!(ResultU8); + storage_hint_works_for_primitive!(ResultU8); type BoxU8 = Box; - item_works_for_primitive!(BoxU8); + storage_hint_works_for_primitive!(BoxU8); type BoxOptionU8 = Box>; - item_works_for_primitive!(BoxOptionU8); + storage_hint_works_for_primitive!(BoxOptionU8); } mod tuples { - use crate::item_works_for_primitive; + use crate::storage_hint_works_for_primitive; type TupleSix = (i32, u32, String, u8, bool, Box>); - item_works_for_primitive!(TupleSix); + storage_hint_works_for_primitive!(TupleSix); } } diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index f8d206f86ad..5dfa6c4e1cf 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -43,10 +43,10 @@ pub use self::{ ResolverKey, }, storage::{ - AutoItem, - Item, + AutoStorableHint, OnCallInitializer, Packed, + StorableHint, StorageKey, }, }; @@ -55,7 +55,7 @@ use ink_primitives::{ Key, }; pub use ink_storage_derive::{ - Item, + StorableHint, StorageKey, StorageLayout, }; diff --git a/crates/storage/src/traits/storage.rs b/crates/storage/src/traits/storage.rs index e50c8262ddb..eb3d35b4515 100644 --- a/crates/storage/src/traits/storage.rs +++ b/crates/storage/src/traits/storage.rs @@ -53,7 +53,7 @@ pub trait StorageKey { /// /// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types /// via blank implementation. -pub trait Item { +pub trait StorableHint { /// Storable type with storage key inside. type Type: Storable; /// The storage key that the type prefers. It can be overwritten by an auto-generated storage key. @@ -63,7 +63,7 @@ pub trait Item { /// Automatically returns the type that should be used for storing the value. /// /// The trait is used by codegen to determine which storage key the type should have. -pub trait AutoItem { +pub trait AutoStorableHint { /// Storable type with storage key inside. type Type: Storable; } From 8eb4cb9c2bb9be3f1efc6241acd83b44ae44da84 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 19 Aug 2022 17:20:05 +0100 Subject: [PATCH 24/40] Fix test --- .../fail/collections_only_packed_2.stderr | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 index d7da1c08c88..54481701ff1 100644 --- 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 @@ -6,7 +6,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` error[E0277]: the trait bound `BTreeMap: Encode` is not satisfied @@ -17,7 +17,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` error[E0277]: the trait bound `BTreeMap: Decode` is not satisfied @@ -28,7 +28,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 @@ -50,7 +50,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 @@ -72,7 +72,7 @@ error[E0277]: the trait bound `BTreeMap: Decode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 @@ -94,7 +94,7 @@ error[E0277]: the trait bound `BTreeMap: Encode` is not satisfi | = 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 `StorableHint<()>` for `BTreeMap` = note: required because of the requirements on the impl of `AutoStorableHint>` for `BTreeMap` note: required because it appears within the type `Contract` --> tests/ui/storage_item/fail/collections_only_packed_2.rs:10:8 From 66a69015e1f9b9e3634fdcea116c395c77efcaad Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 22 Aug 2022 10:58:48 +0100 Subject: [PATCH 25/40] Renamed key_holder. Add UI test for double storage_item. Applied suggestion from the review. --- crates/lang/ir/src/ir/config.rs | 20 +++----- crates/lang/ir/src/ir/storage_item/config.rs | 17 +++---- crates/lang/ir/src/ir/storage_item/mod.rs | 14 ++++++ crates/lang/ir/src/ir/trait_def/config.rs | 20 +++----- crates/lang/macro/src/lib.rs | 49 +++++++++---------- .../storage_item/fail/double_storage_item.rs | 10 ++++ .../fail/double_storage_item.stderr | 5 ++ crates/primitives/derive/src/storable.rs | 21 ++++---- crates/storage/derive/src/lib.rs | 7 ++- .../src/{key_holder.rs => storage_key.rs} | 7 +-- crates/storage/derive/src/storage_layout.rs | 21 ++++---- crates/storage/derive/src/tests/mod.rs | 2 +- .../tests/{key_holder.rs => storage_key.rs} | 20 ++++---- 13 files changed, 112 insertions(+), 101 deletions(-) create mode 100644 crates/lang/tests/ui/storage_item/fail/double_storage_item.rs create mode 100644 crates/lang/tests/ui/storage_item/fail/double_storage_item.stderr rename crates/storage/derive/src/{key_holder.rs => storage_key.rs} (90%) rename crates/storage/derive/src/tests/{key_holder.rs => storage_key.rs} (94%) diff --git a/crates/lang/ir/src/ir/config.rs b/crates/lang/ir/src/ir/config.rs index 1550b8447c2..2078f80fbd3 100644 --- a/crates/lang/ir/src/ir/config.rs +++ b/crates/lang/ir/src/ir/config.rs @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - ast, - ast::MetaNameValue, - error::ExtError as _, -}; +use crate::{ast, ast::MetaNameValue, error::ExtError as _}; use std::collections::HashMap; use syn::spanned::Spanned; @@ -73,7 +69,7 @@ impl WhitelistedAttributes { arg, "expected a string with attributes separated by `,`", )) - } + }; } /// Returns the filtered input vector of whitelisted attributes. @@ -93,18 +89,18 @@ impl WhitelistedAttributes { } /// Return an error to notify about duplicate ink! configuration arguments. -fn duplicate_config_err(fst: F, snd: S, name: &str) -> syn::Error +fn duplicate_config_err(first: F, second: S, name: &str) -> syn::Error where F: Spanned, S: Spanned, { format_err!( - snd.span(), + second.span(), "encountered duplicate ink! `{}` configuration argument", name, ) .into_combine(format_err!( - fst.span(), + first.span(), "first `{}` configuration argument here", name )) @@ -120,7 +116,7 @@ impl TryFrom for Config { for arg in args.into_iter() { if arg.name.is_ident("env") { if let Some((_, ast)) = env { - return Err(duplicate_config_err(ast, arg, "env")) + return Err(duplicate_config_err(ast, arg, "env")); } if let ast::PathOrLit::Path(path) = &arg.value { env = Some((Environment { path: path.clone() }, arg)) @@ -128,7 +124,7 @@ impl TryFrom for Config { return Err(format_err_spanned!( arg, "expected a path for `env` ink! configuration argument", - )) + )); } } else if arg.name.is_ident("keep_attr") { whitelisted_attributes.parse_arg_value(&arg)?; @@ -136,7 +132,7 @@ impl TryFrom for Config { return Err(format_err_spanned!( arg, "encountered unknown or unsupported ink! configuration argument", - )) + )); } } Ok(Config { diff --git a/crates/lang/ir/src/ir/storage_item/config.rs b/crates/lang/ir/src/ir/storage_item/config.rs index 3a58b85aff7..5443dccc23d 100644 --- a/crates/lang/ir/src/ir/storage_item/config.rs +++ b/crates/lang/ir/src/ir/storage_item/config.rs @@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - ast, - error::ExtError as _, -}; +use crate::{ast, error::ExtError as _}; use syn::spanned::Spanned; /// The ink! configuration. @@ -29,18 +26,18 @@ pub struct StorageItemConfig { } /// Return an error to notify about duplicate ink! ink storage configuration arguments. -fn duplicate_config_err(fst: F, snd: S, name: &str) -> syn::Error +fn duplicate_config_err(first: F, second: S, name: &str) -> syn::Error where F: Spanned, S: Spanned, { format_err!( - snd.span(), + second.span(), "encountered duplicate ink! storage item `{}` configuration argument", name, ) .into_combine(format_err!( - fst.span(), + first.span(), "first `{}` configuration argument here", name )) @@ -54,7 +51,7 @@ impl TryFrom for StorageItemConfig { 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")) + 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()) @@ -62,13 +59,13 @@ impl TryFrom for StorageItemConfig { 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 { diff --git a/crates/lang/ir/src/ir/storage_item/mod.rs b/crates/lang/ir/src/ir/storage_item/mod.rs index 2ed1ce85b8a..d651b06a7dc 100644 --- a/crates/lang/ir/src/ir/storage_item/mod.rs +++ b/crates/lang/ir/src/ir/storage_item/mod.rs @@ -35,6 +35,20 @@ impl StorageItem { let parsed_config = syn::parse2::(config)?; let config = StorageItemConfig::try_from(parsed_config)?; + for attr in &ast.attrs { + if attr + .path + .to_token_stream() + .to_string() + .contains("storage_item") + { + return Err(format_err_spanned!( + attr, + "only one `ink::storage_item` is allowed", + )) + } + } + Ok(Self { ast, config }) } diff --git a/crates/lang/ir/src/ir/trait_def/config.rs b/crates/lang/ir/src/ir/trait_def/config.rs index a7b82e231e0..78ec5de4dbc 100644 --- a/crates/lang/ir/src/ir/trait_def/config.rs +++ b/crates/lang/ir/src/ir/trait_def/config.rs @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ - ast, - error::ExtError as _, - ir::config::WhitelistedAttributes, -}; +use crate::{ast, error::ExtError as _, ir::config::WhitelistedAttributes}; use syn::spanned::Spanned; /// The ink! configuration. @@ -49,18 +45,18 @@ impl TraitDefinitionConfig { } /// Return an error to notify about duplicate ink! trait definition configuration arguments. -fn duplicate_config_err(fst: F, snd: S, name: &str) -> syn::Error +fn duplicate_config_err(first: F, second: S, name: &str) -> syn::Error where F: Spanned, S: Spanned, { format_err!( - snd.span(), + second.span(), "encountered duplicate ink! trait definition `{}` configuration argument", name, ) .into_combine(format_err!( - fst.span(), + first.span(), "first `{}` configuration argument here", name )) @@ -75,21 +71,21 @@ impl TryFrom for TraitDefinitionConfig { for arg in args.into_iter() { if arg.name.is_ident("namespace") { if let Some((_, meta_name_value)) = namespace { - return Err(duplicate_config_err(meta_name_value, arg, "namespace")) + return Err(duplicate_config_err(meta_name_value, arg, "namespace")); } if let ast::PathOrLit::Lit(syn::Lit::Str(lit_str)) = &arg.value { if syn::parse_str::(&lit_str.value()).is_err() { return Err(format_err_spanned!( lit_str, "encountered invalid Rust identifier for the ink! namespace configuration parameter" - )) + )); } namespace = Some((lit_str.clone(), arg)) } else { return Err(format_err_spanned!( arg, "expected a string literal for `namespace` ink! trait definition configuration argument", - )) + )); } } else if arg.name.is_ident("keep_attr") { whitelisted_attributes.parse_arg_value(&arg)?; @@ -97,7 +93,7 @@ impl TryFrom for TraitDefinitionConfig { return Err(format_err_spanned!( arg, "encountered unknown or unsupported ink! trait definition configuration argument", - )) + )); } } Ok(TraitDefinitionConfig { diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index ba8dfb9b132..4d051d98fb7 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -668,16 +668,20 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// 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 +/// [`Packed`](ink_storage::traits::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 +/// Consider annotating structs and enums that are intended 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. +/// calculation via `ink::storage_item`. +/// +/// The usage of `KEY: StorageKey` generic allows to propagate the parent's storage key to the type +/// and offset the storage key of the type. It is helpful for non-packed types that can be used +/// several times in the contract. Each field should have a unique storage key, so propagation of +/// the parent's storage key allows one to achieve it. /// /// The macro should be called before `derive` macros because it can change the type. /// @@ -702,6 +706,8 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// }; /// use ink_primitives::traits::Storable; /// +/// // Deriving `scale::Decode` and `scale::Encode` also derives blanket implementation of all +/// // required traits to be storable. /// #[derive(scale::Decode, scale::Encode)] /// struct Packed { /// s1: u128, @@ -710,18 +716,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// // 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 `StorableHint` is only implemented for `Vec` where `T: Packed`. -/// // s3: Vec, -/// } -/// +/// // Example of how to define the packed type with generic. /// #[derive(scale::Decode, scale::Encode)] /// #[cfg_attr( /// feature = "std", @@ -733,6 +728,14 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// s3: String, /// } /// +/// // Example of how to define the non-packed type. +/// #[ink_lang::storage_item] +/// struct NonPacked { +/// s1: Mapping, +/// s2: Lazy, +/// } +/// +/// // Example of how to define the non-packed generic type. /// #[ink_lang::storage_item(derive = false)] /// #[derive(Storable, StorableHint, StorageKey)] /// #[cfg_attr( @@ -745,6 +748,7 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// s3: Mapping, /// } /// +/// // Example of how to define a complex packed type. /// #[derive(scale::Decode, scale::Encode)] /// #[cfg_attr( /// feature = "std", @@ -753,15 +757,10 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// struct PackedComplex { /// s1: u128, /// s2: Vec, -/// s3: Vec, -/// } -/// -/// #[ink_lang::storage_item] -/// struct NonPacked { -/// s1: Mapping, -/// s2: Lazy, +/// s3: Vec, /// } /// +/// // Example of how to define a complex non-packed type. /// #[ink_lang::storage_item] /// struct NonPackedComplex { /// s1: (String, u128, Packed), @@ -778,8 +777,8 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// /// ## Header Arguments /// -/// The `#[ink::storage_item]` macro can be provided with some additional comma-separated -/// header arguments: +/// The `#[ink::storage_item]` macro can be provided with an additional comma-separated +/// header argument: /// /// - `derive: bool` /// diff --git a/crates/lang/tests/ui/storage_item/fail/double_storage_item.rs b/crates/lang/tests/ui/storage_item/fail/double_storage_item.rs new file mode 100644 index 00000000000..3d59cd73176 --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/double_storage_item.rs @@ -0,0 +1,10 @@ +#[ink_lang::storage_item(derive = false)] +#[ink_lang::storage_item(derive = true)] +#[derive(Default)] +struct Contract> { + a: u16, + b: u64, + c: u128, +} + +fn main() {} diff --git a/crates/lang/tests/ui/storage_item/fail/double_storage_item.stderr b/crates/lang/tests/ui/storage_item/fail/double_storage_item.stderr new file mode 100644 index 00000000000..64e01f84b3f --- /dev/null +++ b/crates/lang/tests/ui/storage_item/fail/double_storage_item.stderr @@ -0,0 +1,5 @@ +error: only one `ink::storage_item` is allowed + --> tests/ui/storage_item/fail/double_storage_item.rs:2:1 + | +2 | #[ink_lang::storage_item(derive = true)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/crates/primitives/derive/src/storable.rs b/crates/primitives/derive/src/storable.rs index 1ddbfdf8eb0..d214d56ab8e 100644 --- a/crates/primitives/derive/src/storable.rs +++ b/crates/primitives/derive/src/storable.rs @@ -60,6 +60,15 @@ fn storable_enum_derive(s: &synstructure::Structure) -> TokenStream2 { !s.variants().is_empty(), "encountered invalid empty enum type deriving Storable trait" ); + + if s.variants().len() > 256 { + return syn::Error::new( + s.ast().span(), + "Currently only enums with at most 256 variants are supported.", + ) + .to_compile_error() + } + let decode_body = s .variants() .iter() @@ -132,17 +141,7 @@ pub fn storable_derive(mut s: synstructure::Structure) -> TokenStream2 { .underscore_const(true); match &s.ast().data { syn::Data::Struct(_) => storable_struct_derive(&s), - syn::Data::Enum(data) => { - if s.variants().len() > 256 { - return syn::Error::new( - data.variants.span(), - "Currently only enums with at most 256 variants are supported.", - ) - .to_compile_error() - } - - storable_enum_derive(&s) - } + syn::Data::Enum(_) => storable_enum_derive(&s), _ => { panic!("cannot derive `Storable` for Rust `union` items") } diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index eb2af72c2b3..82f8d91c902 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -19,16 +19,15 @@ extern crate proc_macro; -mod key_holder; mod storable_hint; +mod storage_key; mod storage_layout; #[cfg(test)] mod tests; use self::{ - key_holder::key_holder_derive, - storable_hint::storable_hint_derive, + storable_hint::storable_hint_derive, storage_key::storage_key_derive, storage_layout::storage_layout_derive, }; synstructure::decl_derive!( @@ -95,7 +94,7 @@ synstructure::decl_derive!( /// assert_eq!( as StorageKey>::KEY, 0); /// assert_eq!(> as StorageKey>::KEY, 123); /// ``` - key_holder_derive + storage_key_derive ); synstructure::decl_derive!( [StorageLayout] => diff --git a/crates/storage/derive/src/key_holder.rs b/crates/storage/derive/src/storage_key.rs similarity index 90% rename from crates/storage/derive/src/key_holder.rs rename to crates/storage/derive/src/storage_key.rs index 70aba6e1b89..5e7b4880dab 100644 --- a/crates/storage/derive/src/key_holder.rs +++ b/crates/storage/derive/src/storage_key.rs @@ -14,12 +14,9 @@ use ink_storage_codegen::DeriveUtils; use proc_macro2::TokenStream as TokenStream2; -use quote::{ - quote, - ToTokens, -}; +use quote::{quote, ToTokens}; -pub fn key_holder_derive(mut s: synstructure::Structure) -> TokenStream2 { +pub fn storage_key_derive(mut s: synstructure::Structure) -> TokenStream2 { s.add_bounds(synstructure::AddBounds::None) .underscore_const(true); diff --git a/crates/storage/derive/src/storage_layout.rs b/crates/storage/derive/src/storage_layout.rs index fcc3ea376f4..c42e43ae17b 100644 --- a/crates/storage/derive/src/storage_layout.rs +++ b/crates/storage/derive/src/storage_layout.rs @@ -73,6 +73,15 @@ fn storage_layout_enum(s: &synstructure::Structure) -> TokenStream2 { matches!(s.ast().data, syn::Data::Enum(_)), "s must be an enum item" ); + + if s.variants().len() > 256 { + return syn::Error::new( + s.ast().span(), + "Currently only enums with at most 256 variants are supported.", + ) + .to_compile_error() + } + let variant_layouts = s.variants().iter().enumerate().map(|(n, variant)| { let variant_ident = variant.ast().ident; let discriminant = variant @@ -120,17 +129,7 @@ pub fn storage_layout_derive(mut s: synstructure::Structure) -> TokenStream2 { .underscore_const(true); match &s.ast().data { syn::Data::Struct(_) => storage_layout_struct(&s), - syn::Data::Enum(data) => { - if s.variants().len() > 256 { - return syn::Error::new( - data.variants.span(), - "Currently only enums with at most 256 variants are supported.", - ) - .to_compile_error() - } - - storage_layout_enum(&s) - } + syn::Data::Enum(_) => storage_layout_enum(&s), _ => panic!("cannot derive `StorageLayout` for Rust `union` items"), } } diff --git a/crates/storage/derive/src/tests/mod.rs b/crates/storage/derive/src/tests/mod.rs index 4f605461045..fbd927946cd 100644 --- a/crates/storage/derive/src/tests/mod.rs +++ b/crates/storage/derive/src/tests/mod.rs @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod key_holder; mod storable_hint; +mod storage_key; mod storage_layout; #[macro_export] diff --git a/crates/storage/derive/src/tests/key_holder.rs b/crates/storage/derive/src/tests/storage_key.rs similarity index 94% rename from crates/storage/derive/src/tests/key_holder.rs rename to crates/storage/derive/src/tests/storage_key.rs index 8a09409177f..4c0e6bdae5b 100644 --- a/crates/storage/derive/src/tests/key_holder.rs +++ b/crates/storage/derive/src/tests/storage_key.rs @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::key_holder_derive; +use crate::storage_key_derive; #[test] fn unit_struct_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct UnitStruct; } expands to { @@ -34,7 +34,7 @@ fn unit_struct_works() { #[test] fn unit_struct_generic_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct UnitStruct; } expands to { @@ -51,7 +51,7 @@ fn unit_struct_generic_works() { #[test] fn unit_struct_salt_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct UnitStruct; } expands to { @@ -68,7 +68,7 @@ fn unit_struct_salt_works() { #[test] fn struct_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct NamedFields { a: i32, b: [u8; 32], @@ -89,7 +89,7 @@ fn struct_works() { #[test] fn struct_generic_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct NamedFields { a: T, b: [u8; 32], @@ -110,7 +110,7 @@ fn struct_generic_works() { #[test] fn struct_salt_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { struct NamedFields { a: i32, b: [u8; 32], @@ -131,7 +131,7 @@ fn struct_salt_works() { #[test] fn enum_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { enum MixedEnum { A, B(i32, [u8; 32]), @@ -152,7 +152,7 @@ fn enum_works() { #[test] fn enum_generic_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { enum MixedEnum { A, B(T, [u8; 32]), @@ -173,7 +173,7 @@ fn enum_generic_works() { #[test] fn enum_salt_works() { crate::test_derive! { - key_holder_derive { + storage_key_derive { enum MixedEnum { A, B(u32, [u8; 32]), From 8854be81c6c58fcd86fca37b579cdb0a99bd2315 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 22 Aug 2022 10:59:31 +0100 Subject: [PATCH 26/40] Nightly fmt --- crates/lang/ir/src/ir/config.rs | 14 +++++++++----- crates/lang/ir/src/ir/storage_item/config.rs | 7 +++++-- crates/lang/ir/src/ir/trait_def/config.rs | 8 ++++++-- crates/storage/derive/src/lib.rs | 3 ++- crates/storage/derive/src/storage_key.rs | 5 ++++- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/crates/lang/ir/src/ir/config.rs b/crates/lang/ir/src/ir/config.rs index 2078f80fbd3..a32ff98c3d8 100644 --- a/crates/lang/ir/src/ir/config.rs +++ b/crates/lang/ir/src/ir/config.rs @@ -12,7 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ast, ast::MetaNameValue, error::ExtError as _}; +use crate::{ + ast, + ast::MetaNameValue, + error::ExtError as _, +}; use std::collections::HashMap; use syn::spanned::Spanned; @@ -69,7 +73,7 @@ impl WhitelistedAttributes { arg, "expected a string with attributes separated by `,`", )) - }; + } } /// Returns the filtered input vector of whitelisted attributes. @@ -116,7 +120,7 @@ impl TryFrom for Config { for arg in args.into_iter() { if arg.name.is_ident("env") { if let Some((_, ast)) = env { - return Err(duplicate_config_err(ast, arg, "env")); + return Err(duplicate_config_err(ast, arg, "env")) } if let ast::PathOrLit::Path(path) = &arg.value { env = Some((Environment { path: path.clone() }, arg)) @@ -124,7 +128,7 @@ impl TryFrom for Config { return Err(format_err_spanned!( arg, "expected a path for `env` ink! configuration argument", - )); + )) } } else if arg.name.is_ident("keep_attr") { whitelisted_attributes.parse_arg_value(&arg)?; @@ -132,7 +136,7 @@ impl TryFrom for Config { return Err(format_err_spanned!( arg, "encountered unknown or unsupported ink! configuration argument", - )); + )) } } Ok(Config { diff --git a/crates/lang/ir/src/ir/storage_item/config.rs b/crates/lang/ir/src/ir/storage_item/config.rs index 5443dccc23d..d6c99c6727d 100644 --- a/crates/lang/ir/src/ir/storage_item/config.rs +++ b/crates/lang/ir/src/ir/storage_item/config.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ast, error::ExtError as _}; +use crate::{ + ast, + error::ExtError as _, +}; use syn::spanned::Spanned; /// The ink! configuration. @@ -51,7 +54,7 @@ impl TryFrom for StorageItemConfig { 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")); + 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()) diff --git a/crates/lang/ir/src/ir/trait_def/config.rs b/crates/lang/ir/src/ir/trait_def/config.rs index 78ec5de4dbc..a6a150f4e0a 100644 --- a/crates/lang/ir/src/ir/trait_def/config.rs +++ b/crates/lang/ir/src/ir/trait_def/config.rs @@ -12,7 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{ast, error::ExtError as _, ir::config::WhitelistedAttributes}; +use crate::{ + ast, + error::ExtError as _, + ir::config::WhitelistedAttributes, +}; use syn::spanned::Spanned; /// The ink! configuration. @@ -71,7 +75,7 @@ impl TryFrom for TraitDefinitionConfig { for arg in args.into_iter() { if arg.name.is_ident("namespace") { if let Some((_, meta_name_value)) = namespace { - return Err(duplicate_config_err(meta_name_value, arg, "namespace")); + return Err(duplicate_config_err(meta_name_value, arg, "namespace")) } if let ast::PathOrLit::Lit(syn::Lit::Str(lit_str)) = &arg.value { if syn::parse_str::(&lit_str.value()).is_err() { diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index 82f8d91c902..bc6d08e2cb7 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -27,7 +27,8 @@ mod storage_layout; mod tests; use self::{ - storable_hint::storable_hint_derive, storage_key::storage_key_derive, + storable_hint::storable_hint_derive, + storage_key::storage_key_derive, storage_layout::storage_layout_derive, }; synstructure::decl_derive!( diff --git a/crates/storage/derive/src/storage_key.rs b/crates/storage/derive/src/storage_key.rs index 5e7b4880dab..0cf0e479bd7 100644 --- a/crates/storage/derive/src/storage_key.rs +++ b/crates/storage/derive/src/storage_key.rs @@ -14,7 +14,10 @@ use ink_storage_codegen::DeriveUtils; use proc_macro2::TokenStream as TokenStream2; -use quote::{quote, ToTokens}; +use quote::{ + quote, + ToTokens, +}; pub fn storage_key_derive(mut s: synstructure::Structure) -> TokenStream2 { s.add_bounds(synstructure::AddBounds::None) From 15563deb66cfca80dd393af1e620fcdba22f2cb2 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 22 Aug 2022 11:10:59 +0100 Subject: [PATCH 27/40] Remove `Packed` path --- crates/lang/macro/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index 4d051d98fb7..323ae641f53 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -668,8 +668,8 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// 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`](ink_storage::traits::Packed) types don't have a storage key, but non-packed types -/// (like `Mapping`, `Lazy` etc.) require calculating the storage key during compilation. +/// `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 intended to be a part of /// the storage with this macro. If the type is packed then the usage of the From 4f35230be9851b8c8d819ec8754a2d8150870838 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Mon, 22 Aug 2022 11:37:14 +0100 Subject: [PATCH 28/40] Fix doc test --- crates/lang/macro/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/lang/macro/src/lib.rs b/crates/lang/macro/src/lib.rs index 323ae641f53..1b588eace2c 100644 --- a/crates/lang/macro/src/lib.rs +++ b/crates/lang/macro/src/lib.rs @@ -709,6 +709,10 @@ pub fn trait_definition(attr: TokenStream, item: TokenStream) -> TokenStream { /// // Deriving `scale::Decode` and `scale::Encode` also derives blanket implementation of all /// // required traits to be storable. /// #[derive(scale::Decode, scale::Encode)] +/// #[cfg_attr( +/// feature = "std", +/// derive(scale_info::TypeInfo, ink_storage::traits::StorageLayout) +/// )] /// struct Packed { /// s1: u128, /// s2: Vec, From 5065b07a0e870993d8c0d9b9865cbabe32e9dc23 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 26 Aug 2022 10:34:00 +0100 Subject: [PATCH 29/40] Apply suggestions from hte review --- crates/lang/codegen/src/generator/dispatch.rs | 2 +- crates/lang/src/codegen/dispatch/execution.rs | 10 ++--- .../ui/contract/pass/example-erc20-works.rs | 18 ++++----- crates/metadata/ink-v3-schema.json | 4 +- crates/metadata/src/layout/mod.rs | 39 ++++++++----------- crates/metadata/src/layout/tests.rs | 18 ++++----- crates/metadata/src/layout/validate.rs | 30 +++++++------- crates/storage/derive/src/lib.rs | 11 ++---- crates/storage/src/lazy/mod.rs | 3 +- crates/storage/src/pull_or_init.rs | 25 ++++++------ crates/storage/src/traits/impls/mod.rs | 1 + crates/storage/src/traits/layout/impls.rs | 12 +++--- crates/storage/src/traits/mod.rs | 32 ++------------- crates/storage/src/traits/storage.rs | 17 ++++---- examples/erc20/lib.rs | 13 ++++--- examples/trait-erc20/lib.rs | 18 ++++----- .../delegate-calls/README.md | 10 ++--- 17 files changed, 112 insertions(+), 151 deletions(-) diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 4ab80cb40bd..279d81a08e8 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -798,7 +798,7 @@ impl Dispatch<'_> { fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) { if mutates { - ::ink_storage::traits::push_storage::<#storage_ident>( + ::ink_env::set_contract_storage<::ink_primitives::Key, #storage_ident>( &<#storage_ident as ::ink_storage::traits::StorageKey>::KEY, &contract, ); diff --git a/crates/lang/src/codegen/dispatch/execution.rs b/crates/lang/src/codegen/dispatch/execution.rs index 39814bda425..915d4a98a3d 100644 --- a/crates/lang/src/codegen/dispatch/execution.rs +++ b/crates/lang/src/codegen/dispatch/execution.rs @@ -25,10 +25,7 @@ use ink_env::{ ReturnFlags, }; use ink_primitives::traits::Storable; -use ink_storage::traits::{ - push_storage, - StorageKey, -}; +use ink_storage::traits::StorageKey; use scale::Encode; /// Returns `Ok` if the caller did not transfer additional value to the callee. @@ -68,7 +65,10 @@ where // Constructor is infallible or is fallible but succeeded. // // This requires us to sync back the changes of the contract storage. - push_storage::(&Contract::KEY, contract); + ink_env::set_contract_storage::( + &Contract::KEY, + contract, + ); Ok(()) } Err(_) => { 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 54d25588462..08e74c88651 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -54,22 +54,20 @@ mod erc20 { impl Erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] - pub fn new(initial_supply: Balance) -> Self { - let mut instance = Self::default(); - instance.new_init(initial_supply); - instance - } - - /// Default initializes the ERC-20 contract with the specified initial supply. - fn new_init(&mut self, initial_supply: Balance) { + pub fn new(total_supply: Balance) -> Self { let caller = Self::env().caller(); - self.balances.insert(&caller, &initial_supply); - self.total_supply = initial_supply; + let balances = Default::default(); + balances.insert(&caller, &initial_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), value: initial_supply, }); + Self { + total_supply, + balances, + allowances: Default::default(), + } } /// Returns the total token supply. diff --git a/crates/metadata/ink-v3-schema.json b/crates/metadata/ink-v3-schema.json index 3f0d112c3b5..8af5bf21161 100644 --- a/crates/metadata/ink-v3-schema.json +++ b/crates/metadata/ink-v3-schema.json @@ -97,7 +97,7 @@ } } }, - "CellLayout_for_PortableForm": { + "LeafLayout_for_PortableForm": { "description": "A SCALE encoded cell.", "type": "object", "required": [ @@ -488,7 +488,7 @@ ], "properties": { "cell": { - "$ref": "#/definitions/CellLayout_for_PortableForm" + "$ref": "#/definitions/LeafLayout_for_PortableForm" } }, "additionalProperties": false diff --git a/crates/metadata/src/layout/mod.rs b/crates/metadata/src/layout/mod.rs index 00ce1387b28..b21efaac099 100644 --- a/crates/metadata/src/layout/mod.rs +++ b/crates/metadata/src/layout/mod.rs @@ -58,7 +58,7 @@ pub enum Layout { /// /// This is the only leaf node within the layout graph. /// All layout nodes have this node type as their leafs. - Leaf(CellLayout), + Leaf(LeafLayout), /// The root cell defines the storage key for all sub-trees. Root(RootLayout), /// A layout that hashes values into the entire storage key space. @@ -170,14 +170,14 @@ where serialize = "F::Type: Serialize, F::String: Serialize", deserialize = "F::Type: DeserializeOwned, F::String: DeserializeOwned" ))] -pub struct CellLayout { +pub struct LeafLayout { /// The offset key into the storage. key: LayoutKey, /// The type of the encoded entity. ty: ::Type, } -impl CellLayout { +impl LeafLayout { /// Creates a new cell layout. pub fn new(key: LayoutKey) -> Self where @@ -190,11 +190,11 @@ impl CellLayout { } } -impl IntoPortable for CellLayout { - type Output = CellLayout; +impl IntoPortable for LeafLayout { + type Output = LeafLayout; fn into_portable(self, registry: &mut Registry) -> Self::Output { - CellLayout { + LeafLayout { key: self.key, ty: registry.register_type(&self.ty), } @@ -228,7 +228,7 @@ impl IntoPortable for Layout { } } -impl CellLayout +impl LeafLayout where F: Form, { @@ -641,23 +641,16 @@ impl IntoPortable for EnumLayout { #[derive(Debug, Clone, PartialEq, Eq)] pub enum MetadataError { /// Storage keys of two types intersect - ConflictKey(String, String), + Collision(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 `{}`. \ + Self::Collision(prev_path, curr_path) => { + write!( + f, + "storage key collision occurred for `{}`. \ The same storage key is occupied by the `{}`.", curr_path, if prev_path.is_empty() { @@ -674,14 +667,14 @@ impl MetadataError { #[test] fn valid_error_message() { assert_eq!( - MetadataError::ConflictKey("".to_string(), "Contract.c:".to_string()).to_string(), - "conflict storage key occurred for `Contract.c:`. \ + MetadataError::Collision("".to_string(), "Contract.c:".to_string()).to_string(), + "storage key collision 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()) + MetadataError::Collision("Contract.a:".to_string(), "Contract.c:".to_string()) .to_string(), - "conflict storage key occurred for `Contract.c:`. \ + "storage key collision 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 e71567b35da..464dd446a73 100644 --- a/crates/metadata/src/layout/tests.rs +++ b/crates/metadata/src/layout/tests.rs @@ -26,8 +26,8 @@ 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))), + FieldLayout::new("a", LeafLayout::new::(LayoutKey::from(key))), + FieldLayout::new("b", LeafLayout::new::(LayoutKey::from(key))), ], ) .into() @@ -73,8 +73,8 @@ 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))), + FieldLayout::new("0", LeafLayout::new::(LayoutKey::from(key))), + FieldLayout::new("1", LeafLayout::new::(LayoutKey::from(key))), ], ) .into() @@ -175,11 +175,11 @@ fn mixed_enum_layout(key: &Key) -> Layout { vec![ FieldLayout::new( "0", - CellLayout::new::(LayoutKey::from(variant_key)), + LeafLayout::new::(LayoutKey::from(variant_key)), ), FieldLayout::new( "1", - CellLayout::new::(LayoutKey::from(variant_key)), + LeafLayout::new::(LayoutKey::from(variant_key)), ), ], ), @@ -194,11 +194,11 @@ fn mixed_enum_layout(key: &Key) -> Layout { vec![ FieldLayout::new( "a", - CellLayout::new::(LayoutKey::from(variant_key)), + LeafLayout::new::(LayoutKey::from(variant_key)), ), FieldLayout::new( "b", - CellLayout::new::(LayoutKey::from(variant_key)), + LeafLayout::new::(LayoutKey::from(variant_key)), ), ], ), @@ -287,7 +287,7 @@ fn unbounded_hashing_layout(key: &Key) -> Layout { b"ink storage hashmap".to_vec(), Vec::new(), ), - CellLayout::new::<(i32, bool)>(LayoutKey::from(root_key)), + LeafLayout::new::<(i32, bool)>(LayoutKey::from(root_key)), ) .into() } diff --git a/crates/metadata/src/layout/validate.rs b/crates/metadata/src/layout/validate.rs index 2d11254df81..87d70084810 100644 --- a/crates/metadata/src/layout/validate.rs +++ b/crates/metadata/src/layout/validate.rs @@ -56,7 +56,7 @@ impl ValidateLayout { for variant in en.variants().values() { self.check_struct_layout(variant)?; } - self.name_stack.pop().unwrap(); + self.name_stack.pop().expect("stack is not empty; qed"); Ok(()) } _ => Ok(()), @@ -82,7 +82,7 @@ impl ValidateLayout { 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)) + Err(MetadataError::Collision(prev_path.clone(), path)) } else { self.first_entry.insert(*key, path); Ok(()) @@ -93,10 +93,10 @@ impl ValidateLayout { #[cfg(test)] mod tests { use crate::layout::{ - CellLayout, EnumLayout, FieldLayout, Layout, + LeafLayout, MetadataError, RootLayout, StructLayout, @@ -112,7 +112,7 @@ mod tests { 0.into(), RootLayout::new( 1.into(), - RootLayout::new(2.into(), CellLayout::new::(2.into())), + RootLayout::new(2.into(), LeafLayout::new::(2.into())), ), ); @@ -159,7 +159,7 @@ mod tests { StructLayout::new( "Struct0", vec![ - FieldLayout::new("d", CellLayout::new::(root_0)), + FieldLayout::new("d", LeafLayout::new::(root_0)), FieldLayout::new( "f", RootLayout::new( @@ -180,7 +180,7 @@ mod tests { "g", RootLayout::new( root_4, - CellLayout::new::< + LeafLayout::new::< BTreeSet, >( root_4 @@ -197,7 +197,7 @@ mod tests { "Second", vec![FieldLayout::new( "0", - CellLayout::new::(root_2), + LeafLayout::new::(root_2), )], ), ), @@ -209,7 +209,7 @@ mod tests { "0", RootLayout::new( root_3, - CellLayout::new::( + LeafLayout::new::( root_3, ), ), @@ -223,10 +223,10 @@ mod tests { ], ), ), - FieldLayout::new("b", CellLayout::new::(root_0)), + FieldLayout::new("b", LeafLayout::new::(root_0)), FieldLayout::new( "c", - RootLayout::new(root_1, CellLayout::new::>(root_1)), + RootLayout::new(root_1, LeafLayout::new::>(root_1)), ), ], ), @@ -244,7 +244,7 @@ mod tests { #[test] fn conflict_0_and_1() { assert_eq!( - Err(MetadataError::ConflictKey( + Err(MetadataError::Collision( "".to_string(), "Contract.c:".to_string() )), @@ -255,7 +255,7 @@ mod tests { #[test] fn conflict_0_and_2() { assert_eq!( - Err(MetadataError::ConflictKey( + Err(MetadataError::Collision( "".to_string(), "Contract.a:Struct0.f:".to_string() )), @@ -266,7 +266,7 @@ mod tests { #[test] fn conflict_0_and_3() { assert_eq!( - Err(MetadataError::ConflictKey( + Err(MetadataError::Collision( "".to_string(), "Contract.a:Struct0.f:Enum::Third.0:".to_string() )), @@ -277,7 +277,7 @@ mod tests { #[test] fn conflict_0_and_4() { assert_eq!( - Err(MetadataError::ConflictKey( + Err(MetadataError::Collision( "".to_string(), "Contract.a:Struct0.f:Enum::First.0:Struct1.g:".to_string() )), @@ -288,7 +288,7 @@ mod tests { #[test] fn conflict_3_and_4() { assert_eq!( - Err(MetadataError::ConflictKey( + Err(MetadataError::Collision( "Contract.a:Struct0.f:Enum::First.0:Struct1.g:".to_string(), "Contract.a:Struct0.f:Enum::Third.0:".to_string() )), diff --git a/crates/storage/derive/src/lib.rs b/crates/storage/derive/src/lib.rs index bc6d08e2cb7..aaaa3129412 100644 --- a/crates/storage/derive/src/lib.rs +++ b/crates/storage/derive/src/lib.rs @@ -33,10 +33,9 @@ use self::{ }; synstructure::decl_derive!( [StorableHint] => - /// Derives `ink_storage`'s [`StorableHint`](ink_storage::traits::StorableHint) trait for the given `struct` - /// or `enum`. + /// Derives `ink_storage`'s `StorableHint` trait for the given `struct` or `enum`. /// - /// If the type declaration contains generic [`StorageKey`](ink_storage::traits::StorageKey), + /// If the type declaration contains generic `StorageKey`, /// it will use it as salt to generate a combined storage key. /// /// # Examples @@ -64,8 +63,7 @@ synstructure::decl_derive!( ); synstructure::decl_derive!( [StorageKey] => - /// Derives `ink_storage`'s [`StorageKey`](ink_storage::traits::StorageKey) trait for the given - /// `struct` or `enum`. + /// Derives `ink_storage`'s `StorageKey` trait for the given `struct` or `enum`. /// /// # Examples /// @@ -99,8 +97,7 @@ synstructure::decl_derive!( ); synstructure::decl_derive!( [StorageLayout] => - /// Derives `ink_storage`'s [`StorageLayout`](ink_storage::traits::StorageLayout) trait for the - /// given `struct` or `enum`. + /// Derives `ink_storage`'s `StorageLayout` trait for the given `struct` or `enum`. /// /// # Examples /// diff --git a/crates/storage/src/lazy/mod.rs b/crates/storage/src/lazy/mod.rs index 1cc27f5bbbf..27c2d480172 100644 --- a/crates/storage/src/lazy/mod.rs +++ b/crates/storage/src/lazy/mod.rs @@ -24,7 +24,6 @@ mod mapping; pub use self::mapping::Mapping; use crate::traits::{ - push_storage, AutoKey, StorableHint, StorageKey, @@ -146,7 +145,7 @@ where /// Writes the given `value` to the contract storage. pub fn set(&mut self, value: &V) { - push_storage(&KeyType::KEY, value); + ink_env::set_contract_storage::(&KeyType::KEY, value); } } diff --git a/crates/storage/src/pull_or_init.rs b/crates/storage/src/pull_or_init.rs index 754c45f2bf6..c587765a35d 100644 --- a/crates/storage/src/pull_or_init.rs +++ b/crates/storage/src/pull_or_init.rs @@ -13,17 +13,13 @@ // limitations under the License. //! The module helps during compilation time decide which pull mechanism to use. -//! If the type implements [`OnCallInitializer`](crate::traits::OnCallInitializer) trait, -//! it will use `pull_storage` with combination `OnCallInitializer::initialize`(if the pull failed). -//! Otherwise, it will use only `pull_storage` as a default behavior. +//! If the type implements [`OnCallInitializer`](crate::traits::OnCallInitializer) trait, we will +//! try to pull storage first, on the failure will use `OnCallInitializer::initialize`. //! //! [`OnCallInitializer`](crate::traits::OnCallInitializer) allows initialize the //! type on demand. For more information, check the documentation of the trait. -use crate::traits::{ - pull_storage, - OnCallInitializer, -}; +use crate::traits::OnCallInitializer; use ink_primitives::{ traits::Storable, Key, @@ -55,7 +51,11 @@ impl PullOrInit { pub trait PullOrInitFallback { #[allow(dead_code)] fn pull_or_init(key: &Key) -> T { - pull_storage(key) + match ink_env::get_contract_storage::(key) { + Ok(Some(value)) => value, + Ok(None) => panic!("storage entry was empty"), + Err(_) => panic!("could not properly decode storage entry"), + } } } impl PullOrInitFallback for PullOrInit {} @@ -74,10 +74,7 @@ macro_rules! pull_or_init { #[cfg(test)] mod tests { - use crate::traits::{ - push_storage, - OnCallInitializer, - }; + use crate::traits::OnCallInitializer; use ink_primitives::Key; #[derive(Default, scale::Encode, scale::Decode)] @@ -99,7 +96,7 @@ mod tests { #[ink_lang::test] fn pull_or_init_works() { const KEY: Key = 111; - push_storage(&KEY, &U32(456)); + ink_env::set_contract_storage(&KEY, &U32(456)); let instance = pull_or_init!(U32, KEY); // Instead of init we used a pulled value @@ -117,7 +114,7 @@ mod tests { #[ink_lang::test] fn pull_works() { const KEY: Key = 111; - push_storage(&KEY, &321); + ink_env::set_contract_storage(&KEY, &321); let instance = pull_or_init!(u32, KEY); assert_eq!(321, instance); } diff --git a/crates/storage/src/traits/impls/mod.rs b/crates/storage/src/traits/impls/mod.rs index 101e0212c75..a717f12e07e 100644 --- a/crates/storage/src/traits/impls/mod.rs +++ b/crates/storage/src/traits/impls/mod.rs @@ -90,6 +90,7 @@ where type Type = >>::Type; } +impl

super::storage::private::Sealed for P where P: scale::Decode + scale::Encode {} impl

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

StorageKey for P diff --git a/crates/storage/src/traits/layout/impls.rs b/crates/storage/src/traits/layout/impls.rs index 34a98176b98..7a8367647ee 100644 --- a/crates/storage/src/traits/layout/impls.rs +++ b/crates/storage/src/traits/layout/impls.rs @@ -22,12 +22,12 @@ use ink_env::{ }; use ink_metadata::layout::{ ArrayLayout, - CellLayout, Discriminant, EnumLayout, FieldLayout, Layout, LayoutKey, + LeafLayout, StructLayout, }; use ink_prelude::{ @@ -48,7 +48,7 @@ macro_rules! impl_storage_layout_for_primitives { $( impl StorageLayout for $name { fn layout(key: &Key) -> Layout { - Layout::Leaf(CellLayout::new::<$name>(LayoutKey::from(key))) + Layout::Leaf(LeafLayout::new::<$name>(LayoutKey::from(key))) } } )* @@ -228,7 +228,7 @@ where T: TypeInfo + 'static + Packed, { fn layout(key: &Key) -> Layout { - Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + Layout::Leaf(LeafLayout::new::(LayoutKey::from(key))) } } @@ -238,7 +238,7 @@ where V: TypeInfo + 'static + Packed, { fn layout(key: &Key) -> Layout { - Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + Layout::Leaf(LeafLayout::new::(LayoutKey::from(key))) } } @@ -247,7 +247,7 @@ where T: TypeInfo + 'static + Packed, { fn layout(key: &Key) -> Layout { - Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + Layout::Leaf(LeafLayout::new::(LayoutKey::from(key))) } } @@ -256,6 +256,6 @@ where T: TypeInfo + 'static + Packed, { fn layout(key: &Key) -> Layout { - Layout::Leaf(CellLayout::new::(LayoutKey::from(key))) + Layout::Leaf(LeafLayout::new::(LayoutKey::from(key))) } } diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index 5dfa6c4e1cf..cc300da6325 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -15,15 +15,15 @@ //! Traits and interfaces to operate with storage entities. //! //! Generally a type is said to be a storage entity if it implements the -//! `Storable` trait. This defines certain constants and routines in order +//! [`Storable`] 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 `Packed` shows that the type can be stored into single storage cell. +//! The [`Packed`] shows that the type can be stored into single storage cell. //! In most cases, collections(`Vec`, `HashMap`, `HashSet` etc.) work only with packed structures. //! //! If at least one of the type's fields occupies its own separate storage cell, it is a -//! non-`Packed` type because it occupies more than one storage cell. +//! non-[`Packed`] type because it occupies more than one storage cell. mod impls; mod storage; @@ -59,29 +59,3 @@ pub use ink_storage_derive::{ StorageKey, StorageLayout, }; - -/// Pulls an instance of type `T` from the contract storage given its storage key. -/// -/// # Panics -/// -/// - If the storage entry is empty. -/// - If could not properly decode storage entry. -pub fn pull_storage(key: &Key) -> T -where - T: Storable, -{ - match ink_env::get_contract_storage::(key) { - Ok(Some(value)) => value, - Ok(None) => panic!("storage entry was empty"), - Err(_) => panic!("could not properly decode storage entry"), - } -} - -/// Pushes the entity to the contract storage at the provided storage key and returns the size -/// of pre-existing value if any. -pub fn push_storage(key: &Key, entity: &T) -> Option -where - T: Storable, -{ - ink_env::set_contract_storage::(key, entity) -} diff --git a/crates/storage/src/traits/storage.rs b/crates/storage/src/traits/storage.rs index eb3d35b4515..f7ef7769ef7 100644 --- a/crates/storage/src/traits/storage.rs +++ b/crates/storage/src/traits/storage.rs @@ -17,26 +17,29 @@ use ink_primitives::{ Key, }; +pub(crate) mod private { + /// Seals the implementation of `Packed`. + pub trait Sealed {} +} + /// Trait for describing types that can be read and written to storage while all fields occupy /// only a single storage cell. /// -/// If at least one of the fields in the type occupies its storage cell, this type +/// If at least one of the fields in the type occupies its own storage cell, this type /// is considered 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: Storable + scale::Decode + scale::Encode {} +/// and `scale::Decode` via blanket implementation. +pub trait Packed: Storable + scale::Decode + scale::Encode + private::Sealed {} /// Holds storage key for the type. /// /// # Note /// /// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types -/// via blank implementation. +/// via blanket implementation. pub trait StorageKey { /// Storage key of the type. const KEY: Key; @@ -52,7 +55,7 @@ pub trait StorageKey { /// # Note /// /// The trait is automatically implemented for [`Packed`](crate::traits::Packed) types -/// via blank implementation. +/// via blanket implementation. pub trait StorableHint { /// Storable type with storage key inside. type Type: Storable; diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index c1e3d6eb9ff..028fe8fcba2 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -56,17 +56,20 @@ mod erc20 { impl Erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] - pub fn new(initial_supply: Balance) -> Self { - let mut instance = Erc20::default(); + pub fn new(total_supply: Balance) -> Self { + let balances = Default::default(); let caller = Self::env().caller(); - instance.balances.insert(&caller, &initial_supply); - instance.total_supply = initial_supply; + balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), value: initial_supply, }); - instance + Self { + total_supply, + balances, + allowances: Default::default(), + } } /// Returns the total token supply. diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index bd7715c1afc..ba231c57a8a 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -93,22 +93,20 @@ mod erc20 { impl Erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] - pub fn new(initial_supply: Balance) -> Self { - let mut instance = Self::default(); - instance.new_init(initial_supply); - instance - } - - /// Default initializes the ERC-20 contract with the specified initial supply. - fn new_init(&mut self, initial_supply: Balance) { + pub fn new(total_supply: Balance) -> Self { let caller = Self::env().caller(); - self.balances.insert(&caller, &initial_supply); - self.total_supply = initial_supply; + let balances = Default::default(); + balances.insert(&caller, &initial_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), value: initial_supply, }); + Self { + total_supply, + balances, + allowances: Default::default(), + } } } diff --git a/examples/upgradeable-contracts/delegate-calls/README.md b/examples/upgradeable-contracts/delegate-calls/README.md index 0d85a30818a..860e596aea0 100644 --- a/examples/upgradeable-contracts/delegate-calls/README.md +++ b/examples/upgradeable-contracts/delegate-calls/README.md @@ -17,12 +17,10 @@ In order to test it out you need to do the following: You will receive the respective `upgradeable_flipper.contract` bundle in the `./upgradeable-flipper/target/ink/` folder. - In order to perform migrations and have proxy working for contracts with different - [storage layouts](https://ink.substrate.io/datastructures/spread-storage-layout), - we use the [`Upgradeable`](upgradeable-flipper/upgradeable.rs) type - wrapper, which ensures that we write different fields of desired struct to different - storage locations, while also tracking the initialization status (e.g., we uploaded - the code on chain, but haven't called the constructor). + In order to perform migrations and have proxy working for contracts with different + storage layouts, we implement the `OnCallInitializer` trait that acts as a + constructor during the execution of the contract. It initializes the contract if it was + not initialized. 1. Build the proxy contract: ``` From 63e39097eaaffb2616e98acec4c41989876439f0 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 26 Aug 2022 10:55:00 +0100 Subject: [PATCH 30/40] Fixed build --- crates/lang/codegen/src/generator/dispatch.rs | 2 +- crates/lang/ir/Cargo.toml | 2 +- .../lang/tests/ui/contract/pass/example-erc20-works.rs | 4 ++-- crates/primitives/Cargo.toml | 2 +- crates/primitives/derive/Cargo.toml | 10 +++++----- crates/storage/codegen/Cargo.toml | 4 ++-- crates/storage/derive/Cargo.toml | 2 +- crates/storage/src/traits/mod.rs | 4 ---- examples/erc20/lib.rs | 4 ++-- examples/trait-erc20/lib.rs | 4 ++-- 10 files changed, 17 insertions(+), 21 deletions(-) diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 279d81a08e8..6f7ea20ab06 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -798,7 +798,7 @@ impl Dispatch<'_> { fn push_contract(contract: ::core::mem::ManuallyDrop<#storage_ident>, mutates: bool) { if mutates { - ::ink_env::set_contract_storage<::ink_primitives::Key, #storage_ident>( + ::ink_env::set_contract_storage::<::ink_primitives::Key, #storage_ident>( &<#storage_ident as ::ink_storage::traits::StorageKey>::KEY, &contract, ); diff --git a/crates/lang/ir/Cargo.toml b/crates/lang/ir/Cargo.toml index fd198e4c611..3e89ad57424 100644 --- a/crates/lang/ir/Cargo.toml +++ b/crates/lang/ir/Cargo.toml @@ -18,7 +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" } +ink_storage_codegen = { version = "4.0.0-alpha.1", path = "../../storage/codegen" } quote = "1" syn = { version = "1.0", features = ["parsing", "full", "visit", "extra-traits"] } proc-macro2 = "1.0" 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 08e74c88651..e2ef66d598c 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -55,13 +55,13 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { + let mut balances: Mapping = Default::default(); let caller = Self::env().caller(); - let balances = Default::default(); balances.insert(&caller, &initial_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), - value: initial_supply, + value: total_supply, }); Self { total_supply, diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 3c3f3cac745..ba22a5db0a1 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -20,7 +20,7 @@ scale = { package = "parity-scale-codec", version = "3", default-features = fals scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } sha2-const = { version = "0.1.2", default-features = false } -ink_primitives_derive = { version = "4.0.0", path = "derive", default-features = false } +ink_primitives_derive = { version = "4.0.0-alpha.1", path = "derive", default-features = false } [features] default = ["std"] diff --git a/crates/primitives/derive/Cargo.toml b/crates/primitives/derive/Cargo.toml index fa3c7257711..d2d977d32a2 100644 --- a/crates/primitives/derive/Cargo.toml +++ b/crates/primitives/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ink_primitives_derive" -version = "4.0.0" +version = "4.0.0-alpha.1" authors = ["Parity Technologies "] edition = "2021" @@ -25,7 +25,7 @@ synstructure = "0.12.4" [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 = ".." } -ink_metadata = { version = "4.0.0", path = "../../metadata" } -ink_prelude = { version = "4.0.0", path = "../../prelude/" } +ink_env = { version = "4.0.0-alpha.1", path = "../../env" } +ink_primitives = { version = "4.0.0-alpha.1", path = ".." } +ink_metadata = { version = "4.0.0-alpha.1", path = "../../metadata" } +ink_prelude = { version = "4.0.0-alpha.1", path = "../../prelude/" } diff --git a/crates/storage/codegen/Cargo.toml b/crates/storage/codegen/Cargo.toml index 02b063fb8ac..a7f88bf97ca 100644 --- a/crates/storage/codegen/Cargo.toml +++ b/crates/storage/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ink_storage_codegen" -version = "4.0.0" +version = "4.0.0-alpha.1" authors = ["Parity Technologies "] edition = "2021" @@ -15,5 +15,5 @@ categories = ["no-std", "embedded"] include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"] [dependencies] -ink_primitives = { version = "4.0.0", path = "../../primitives", default-features = false } +ink_primitives = { version = "4.0.0-alpha.1", path = "../../primitives", default-features = false } syn = { version = "1", features = ["full"] } \ No newline at end of file diff --git a/crates/storage/derive/Cargo.toml b/crates/storage/derive/Cargo.toml index af7138b5884..9c3f392c020 100644 --- a/crates/storage/derive/Cargo.toml +++ b/crates/storage/derive/Cargo.toml @@ -22,7 +22,7 @@ quote = "1" syn = { version = "1", features = ["full"] } proc-macro2 = "1" synstructure = "0.12.4" -ink_storage_codegen = { version = "4.0.0", path = "../codegen" } +ink_storage_codegen = { version = "4.0.0-alpha.1", path = "../codegen" } [dev-dependencies] scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "full"] } diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index cc300da6325..121af32ac3b 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -50,10 +50,6 @@ pub use self::{ StorageKey, }, }; -use ink_primitives::{ - traits::Storable, - Key, -}; pub use ink_storage_derive::{ StorableHint, StorageKey, diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index 028fe8fcba2..8c5e08e48c6 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -57,13 +57,13 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { - let balances = Default::default(); + let mut balances: Mapping = Default::default(); let caller = Self::env().caller(); balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), - value: initial_supply, + value: total_supply, }); Self { total_supply, diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index ba231c57a8a..b6e3bb9036e 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -94,13 +94,13 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { + let mut balances: Mapping = Default::default(); let caller = Self::env().caller(); - let balances = Default::default(); balances.insert(&caller, &initial_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), - value: initial_supply, + value: total_supply, }); Self { total_supply, From c07b47102adc4010bef9cf88beb73175d9d03253 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 26 Aug 2022 11:13:54 +0100 Subject: [PATCH 31/40] Fix build --- crates/lang/tests/ui/contract/pass/example-erc20-works.rs | 2 +- examples/trait-erc20/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 e2ef66d598c..f04b00d1dac 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -57,7 +57,7 @@ mod erc20 { pub fn new(total_supply: Balance) -> Self { let mut balances: Mapping = Default::default(); let caller = Self::env().caller(); - balances.insert(&caller, &initial_supply); + balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index b6e3bb9036e..f656ace0e43 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -96,7 +96,7 @@ mod erc20 { pub fn new(total_supply: Balance) -> Self { let mut balances: Mapping = Default::default(); let caller = Self::env().caller(); - balances.insert(&caller, &initial_supply); + balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { from: None, to: Some(caller), From 72c84a816df3c3d37819208d1cb9eb995ae37335 Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 26 Aug 2022 11:25:51 +0100 Subject: [PATCH 32/40] Removed `initialize_contract` from linting and deleted all tests --- linting/Cargo.toml | 31 -- linting/src/lib.rs | 6 +- linting/src/mapping_initialized.rs | 372 ------------------ .../ui/fail/mapping-nested-initialize-call.rs | 61 --- .../mapping-nested-initialize-call.stderr | 27 -- linting/ui/fail/mapping-one-constructor.rs | 50 --- .../ui/fail/mapping-one-constructor.stderr | 26 -- .../ui/fail/mapping-two-constructors-01.rs | 65 --- .../fail/mapping-two-constructors-01.stderr | 28 -- .../ui/fail/mapping-two-constructors-02.rs | 65 --- .../fail/mapping-two-constructors-02.stderr | 29 -- .../mapping-additional-logic-constructor.rs | 64 --- .../mapping-dont-use-fully-qualified-path.rs | 56 --- linting/ui/pass/mapping-one-constructor.rs | 55 --- linting/ui/pass/mapping-two-constructors.rs | 66 ---- 15 files changed, 1 insertion(+), 1000 deletions(-) delete mode 100644 linting/src/mapping_initialized.rs delete mode 100644 linting/ui/fail/mapping-nested-initialize-call.rs delete mode 100644 linting/ui/fail/mapping-nested-initialize-call.stderr delete mode 100644 linting/ui/fail/mapping-one-constructor.rs delete mode 100644 linting/ui/fail/mapping-one-constructor.stderr delete mode 100644 linting/ui/fail/mapping-two-constructors-01.rs delete mode 100644 linting/ui/fail/mapping-two-constructors-01.stderr delete mode 100644 linting/ui/fail/mapping-two-constructors-02.rs delete mode 100644 linting/ui/fail/mapping-two-constructors-02.stderr delete mode 100644 linting/ui/pass/mapping-additional-logic-constructor.rs delete mode 100644 linting/ui/pass/mapping-dont-use-fully-qualified-path.rs delete mode 100644 linting/ui/pass/mapping-one-constructor.rs delete mode 100644 linting/ui/pass/mapping-two-constructors.rs diff --git a/linting/Cargo.toml b/linting/Cargo.toml index 0daf250020d..ae2a2be4ccd 100644 --- a/linting/Cargo.toml +++ b/linting/Cargo.toml @@ -45,37 +45,6 @@ scale-info = { version = "2", default-features = false, features = ["derive"] } # # Those files require the ink! dependencies though, by having # them as examples here, they inherit the `dev-dependencies`. -[[example]] -name = "fail_mapping_one_constructor" -path = "ui/fail/mapping-one-constructor.rs" - -[[example]] -name = "fail_mapping_two_constructors_01" -path = "ui/fail/mapping-two-constructors-01.rs" - -[[example]] -name = "fail_mapping_two_constructors_02" -path = "ui/fail/mapping-two-constructors-02.rs" - -[[example]] -name = "fail_mapping_nested_initialize_call" -path = "ui/fail/mapping-nested-initialize-call.rs" - -[[example]] -name = "pass_mapping_one_constructor" -path = "ui/pass/mapping-one-constructor.rs" - -[[example]] -name = "pass_mapping_two_constructors" -path = "ui/pass/mapping-two-constructors.rs" - -[[example]] -name = "pass_mapping_additional_logic_constructor" -path = "ui/pass/mapping-additional-logic-constructor.rs" - -[[example]] -name = "pass_dont_use_fully_qualified_path" -path = "ui/pass/mapping-dont-use-fully-qualified-path.rs" [package.metadata.rust-analyzer] rustc_private = true diff --git a/linting/src/lib.rs b/linting/src/lib.rs index 97ec87e7f1b..0eed19b2bed 100644 --- a/linting/src/lib.rs +++ b/linting/src/lib.rs @@ -26,16 +26,12 @@ extern crate rustc_middle; extern crate rustc_session; extern crate rustc_span; -mod mapping_initialized; - #[doc(hidden)] #[no_mangle] pub fn register_lints( _sess: &rustc_session::Session, - lint_store: &mut rustc_lint::LintStore, + _lint_store: &mut rustc_lint::LintStore, ) { - lint_store.register_lints(&[mapping_initialized::MAPPING_INITIALIZED]); - lint_store.register_late_pass(|| Box::new(mapping_initialized::MappingInitialized)); } #[test] diff --git a/linting/src/mapping_initialized.rs b/linting/src/mapping_initialized.rs deleted file mode 100644 index 92e9ceaf8f8..00000000000 --- a/linting/src/mapping_initialized.rs +++ /dev/null @@ -1,372 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -use clippy_utils::{ - diagnostics::span_lint_and_then, - source::snippet_opt, -}; -use if_chain::if_chain; -use regex::Regex; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir::{ - def_id::DefId, - intravisit::{ - walk_fn, - walk_item, - walk_qpath, - FnKind, - Visitor, - }, - BodyId, - FnDecl, - HirId, - Item, - ItemKind, - QPath, - VariantData, -}; -use rustc_lint::{ - LateContext, - LateLintPass, -}; -use rustc_middle::hir::nested_filter; -use rustc_session::{ - declare_lint, - declare_lint_pass, -}; -use rustc_span::{ - source_map::Span, - symbol::sym, -}; - -declare_lint! { - /// **What it does:** Checks for ink! contracts that use - /// [`ink_storage::Mapping`](https://paritytech.github.io/ink/ink_storage/struct.Mapping.html) - /// in their storage without initializing it properly in the ink! constructor. - /// - /// **Why is this bad?** If `Mapping` is not properly initialized corruption of storage - /// might occur. - /// - /// **Known problems:** The lint currently requires for the - /// `ink_lang::utils::initialize_contract(…)` function call to be made on the top - /// level of the constructor *and* it has be made explicitly in this form. - /// - /// The lint can currently not detect if `initialize_contract` would be called in a - /// nested function within the constructor. - /// - /// **Example:** - /// - /// ```rust - /// // Good - /// use ink_storage::{traits::SpreadAllocate, Mapping}; - /// - /// #[ink(storage)] - /// #[derive(SpreadAllocate)] - /// pub struct MyContract { - /// balances: Mapping, - /// } - /// - /// #[ink(constructor)] - /// pub fn new() -> Self { - /// ink_lang::utils::initialize_contract(Self::new_init) - /// } - /// - /// /// Default initializes the contract. - /// fn new_init(&mut self) { - /// let caller = Self::env().caller(); - /// let value: Balance = Default::default(); - /// self.balances.insert(&caller, &value); - /// } - /// ``` - /// - /// ```rust - /// // Bad - /// use ink_storage::{traits::SpreadAllocate, Mapping}; - /// - /// #[ink(storage)] - /// #[derive(SpreadAllocate)] - /// pub struct MyContract { - /// balances: Mapping, - /// } - /// - /// #[ink(constructor)] - /// pub fn new() -> Self { - /// Self { - /// balances: Default::default(), - /// } - /// } - /// ``` - pub MAPPING_INITIALIZED, - Deny, - "Error on ink! contracts that use `ink_storage::Mapping` without initializing it." -} - -declare_lint_pass!(MappingInitialized => [MAPPING_INITIALIZED]); - -/// An ink! attribute. -#[derive(PartialEq)] -enum InkAttribute { - // #[ink(storage)] - Storage, - // #[ink(constructor)] - Constructor, -} - -/// Returns `Some(InkAttribute)` if an ink! attribute is among `attributes`. -fn get_ink_attribute(attributes: &[ast::Attribute]) -> Option { - const INK_STORAGE: &str = "__ink_dylint_Storage"; - const INK_CONSTRUCTOR: &str = "__ink_dylint_Constructor"; - - let attrs = format!("symbol: \"{:?}\"", attributes); - if attrs.contains(INK_STORAGE) { - Some(InkAttribute::Storage) - } else if attrs.contains(INK_CONSTRUCTOR) { - Some(InkAttribute::Constructor) - } else { - None - } -} - -impl<'tcx> LateLintPass<'tcx> for MappingInitialized { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let ink_attrs = get_ink_attribute(attrs); - if ink_attrs.is_none() { - return - } - - if let ItemKind::Struct(variant_data, _) = &item.kind { - if let VariantData::Unit(..) = variant_data { - return - } - check_struct(cx, item, variant_data); - } - } -} - -/// Examines a `struct`. If the struct is annotated as an ink! storage struct -/// we examine if there is a `ink_storage::Mapping` in it; in case there -/// is we continue checking all ink! constructors for correct usage of the -/// `ink_lang::utils::initialize_contract` API. -/// -/// This function is backwards compatible, in case no annotated ink! struct is -/// found nothing happens. -fn check_struct<'a>(cx: &LateContext<'a>, item: &'a Item, data: &VariantData) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - match get_ink_attribute(attrs) { - Some(InkAttribute::Storage) => {} - _ => return, - } - - let mut marker = None; - let fields = data.fields(); - let storage_contains_mapping = fields.iter().any(|field| { - let re = Regex::new(r"ink_storage\[.*\]::lazy::mapping::Mapping") - .expect("failed creating regex"); - if re.is_match(&format!("{:?}", field.ty.kind)) { - marker = Some(field); - return true - } - false - }); - - if !storage_contains_mapping { - log::debug!("Found `#[ink(storage)]` struct without `Mapping`"); - return - } - log::debug!("Found `#[ink(storage)]` struct with `Mapping`"); - - let inherent_impls = cx.tcx.inherent_impls(item.def_id); - let constructors_without_initialize: Vec = inherent_impls - .iter() - .map(|imp_did| item_from_def_id(cx, *imp_did)) - .filter_map(|item| constructor_but_no_initialize(cx, item)) - .collect(); - log::debug!( - "Result of searching for constructors without initialize: {:?}", - constructors_without_initialize - ); - - if_chain! { - if storage_contains_mapping; - if !constructors_without_initialize.is_empty(); - then { - let constructor_span = constructors_without_initialize.get(0) - .expect("at least one faulty constructor must exist"); - let snippet = snippet_opt(cx, *constructor_span) - .expect("snippet must exist"); - span_lint_and_then( - cx, - MAPPING_INITIALIZED, - item.span, - &format!( - "`#[ink(storage)]` on `{}` contains `ink_storage::Mapping` without initializing it in the contract constructor.", - item.ident - ), - |diag| { - diag.span_suggestion( - *constructor_span, - "add an `ink_lang::utils::initialize_contract(…)` function in this constructor", - snippet, - Applicability::Unspecified, - ); - diag.span_help(marker.expect("marker must exist").span, "this field uses `ink_storage::Mapping`"); - }); - } - } -} - -/// Returns `Some(span)` if a constructor without a call of -/// `ink_lang::utils::initialize_contract(…)` was found. -fn constructor_but_no_initialize<'tcx>( - cx: &LateContext<'tcx>, - item: &'tcx Item<'_>, -) -> Option { - let mut visitor = InkAttributeVisitor { - cx, - ink_attribute: None, - constructor_info: None, - }; - - walk_item(&mut visitor, item); - - match visitor.constructor_info { - Some(info) if !info.uses_initialize_contract => Some(info.span), - _ => None, - } -} - -/// Visitor for ink! attributes. -struct InkAttributeVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - ink_attribute: Option, - constructor_info: Option, -} - -// Information about an ink! constructor. -struct InkConstructor { - // The constructor has a call to `ink_lang::utils::initialize_contract(…)` - // in its function. - uses_initialize_contract: bool, - // The span for the constructor function. - span: Span, -} - -impl<'tcx> Visitor<'tcx> for InkAttributeVisitor<'_, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - span: Span, - id: HirId, - ) { - // We can return immediately if an incorrect constructor was already found - if let Some(constructor) = &self.constructor_info { - if !constructor.uses_initialize_contract { - return - } - } - - let attrs: Vec = self - .cx - .tcx - .get_attrs(id.owner.to_def_id(), sym::cfg) - .cloned() - .collect(); - self.ink_attribute = get_ink_attribute(&attrs); - - if self.ink_attribute == Some(InkAttribute::Storage) { - return - } else if self.ink_attribute == Some(InkAttribute::Constructor) { - log::debug!( - "Found constructor, starting to search for `initialize_contract`" - ); - let mut visitor = InitializeContractVisitor { - cx: self.cx, - uses_initialize_contract: false, - }; - walk_fn(&mut visitor, kind, decl, body_id, span, id); - - log::debug!( - "Has `initialize_contract`? {:?}", - visitor.uses_initialize_contract - ); - self.constructor_info = Some(InkConstructor { - uses_initialize_contract: visitor.uses_initialize_contract, - span, - }); - - return - } - - walk_fn(self, kind, decl, body_id, span, id); - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -/// Visitor to determine if a `fn` contains a call to `ink_lang::utils::initialize_contract(…)`. -/// -/// # Known Limitation -/// -/// This function currently only finds call to `initialize_contract` which happen -/// on the first level of the `fn` ‒ no nested calls are found! So if you would -/// call `initialize_contract` within a sub-function of the ink! constructor -/// this is not recognized! -struct InitializeContractVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - uses_initialize_contract: bool, -} - -impl<'tcx> Visitor<'tcx> for InitializeContractVisitor<'_, 'tcx> { - type NestedFilter = nested_filter::All; - - fn visit_qpath(&mut self, qpath: &'tcx QPath<'_>, id: HirId, span: Span) { - log::debug!("Visiting path {:?}", qpath); - if self.uses_initialize_contract { - return - } - - if let QPath::Resolved(_, path) = qpath { - log::debug!("QPath: {:?}", path.res); - let re = Regex::new( - r"ink_lang\[.*\]::codegen::dispatch::execution::initialize_contract", - ) - .expect("failed creating regex"); - if re.is_match(&format!("{:?}", path.res)) { - self.uses_initialize_contract = true; - return - } - } - - walk_qpath(self, qpath, id, span); - } - - fn nested_visit_map(&mut self) -> Self::Map { - self.cx.tcx.hir() - } -} - -/// Returns the `rustc_hir::Item` for a `rustc_hir::def_id::DefId`. -fn item_from_def_id<'tcx>(cx: &LateContext<'tcx>, def_id: DefId) -> &'tcx Item<'tcx> { - cx.tcx.hir().expect_item(def_id.expect_local()) -} diff --git a/linting/ui/fail/mapping-nested-initialize-call.rs b/linting/ui/fail/mapping-nested-initialize-call.rs deleted file mode 100644 index b9086a65ac7..00000000000 --- a/linting/ui/fail/mapping-nested-initialize-call.rs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - /// The linter currently does not detect if `initialize_contract` is - /// called in a nested function. - #[ink(constructor)] - pub fn new1() -> Self { - Self::foo() - } - - fn foo() -> Self { - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/fail/mapping-nested-initialize-call.stderr b/linting/ui/fail/mapping-nested-initialize-call.stderr deleted file mode 100644 index fc9b47bb24d..00000000000 --- a/linting/ui/fail/mapping-nested-initialize-call.stderr +++ /dev/null @@ -1,27 +0,0 @@ -error: `#[ink(storage)]` on `MyContract` contains `ink_storage::Mapping` without initializing it in the contract constructor. - --> $DIR/mapping-nested-initialize-call.rs:29:5 - | -LL | / #[derive(SpreadAllocate)] -LL | | pub struct MyContract { -LL | | balances: Mapping, -LL | | } - | |_____^ - | - = note: `#[deny(mapping_initialized)]` on by default -help: this field uses `ink_storage::Mapping` - --> $DIR/mapping-nested-initialize-call.rs:31:9 - | -LL | balances: Mapping, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor - | -LL ~ /// The linter currently does not detect if `initialize_contract` is -LL + /// called in a nested function. -LL + #[ink(constructor)] -LL + pub fn new1() -> Self { -LL + Self::foo() -LL + } - | - -error: aborting due to previous error - diff --git a/linting/ui/fail/mapping-one-constructor.rs b/linting/ui/fail/mapping-one-constructor.rs deleted file mode 100644 index 8c608199546..00000000000 --- a/linting/ui/fail/mapping-one-constructor.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - #[ink(constructor)] - pub fn new() -> Self { - Self { - balances: Default::default(), - } - } - - /// Returns the total token supply. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/fail/mapping-one-constructor.stderr b/linting/ui/fail/mapping-one-constructor.stderr deleted file mode 100644 index b78ff9ffeef..00000000000 --- a/linting/ui/fail/mapping-one-constructor.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error: `#[ink(storage)]` on `MyContract` contains `ink_storage::Mapping` without initializing it in the contract constructor. - --> $DIR/mapping-one-constructor.rs:29:5 - | -LL | / #[derive(SpreadAllocate)] -LL | | pub struct MyContract { -LL | | balances: Mapping, -LL | | } - | |_____^ - | - = note: `#[deny(mapping_initialized)]` on by default -help: this field uses `ink_storage::Mapping` - --> $DIR/mapping-one-constructor.rs:31:9 - | -LL | balances: Mapping, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor - | -LL ~ pub fn new() -> Self { -LL + Self { -LL + balances: Default::default(), -LL + } -LL + } - | - -error: aborting due to previous error - diff --git a/linting/ui/fail/mapping-two-constructors-01.rs b/linting/ui/fail/mapping-two-constructors-01.rs deleted file mode 100644 index 4c2e426bf22..00000000000 --- a/linting/ui/fail/mapping-two-constructors-01.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - /// This constructor is correct. - /// It comes before the incorrect constructor. - #[ink(constructor)] - pub fn new1() -> Self { - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// This constructor is **not** correct. - #[ink(constructor)] - pub fn new2() -> Self { - Self { - balances: Default::default(), - } - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/fail/mapping-two-constructors-01.stderr b/linting/ui/fail/mapping-two-constructors-01.stderr deleted file mode 100644 index 2c291635c55..00000000000 --- a/linting/ui/fail/mapping-two-constructors-01.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: `#[ink(storage)]` on `MyContract` contains `ink_storage::Mapping` without initializing it in the contract constructor. - --> $DIR/mapping-two-constructors-01.rs:29:5 - | -LL | / #[derive(SpreadAllocate)] -LL | | pub struct MyContract { -LL | | balances: Mapping, -LL | | } - | |_____^ - | - = note: `#[deny(mapping_initialized)]` on by default -help: this field uses `ink_storage::Mapping` - --> $DIR/mapping-two-constructors-01.rs:31:9 - | -LL | balances: Mapping, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor - | -LL ~ /// This constructor is **not** correct. -LL + #[ink(constructor)] -LL + pub fn new2() -> Self { -LL + Self { -LL + balances: Default::default(), -LL + } -LL + } - | - -error: aborting due to previous error - diff --git a/linting/ui/fail/mapping-two-constructors-02.rs b/linting/ui/fail/mapping-two-constructors-02.rs deleted file mode 100644 index a92f4952be7..00000000000 --- a/linting/ui/fail/mapping-two-constructors-02.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - /// This constructor is **not** correct. - /// It comes before the correct constructor. - #[ink(constructor)] - pub fn new2() -> Self { - Self { - balances: Default::default(), - } - } - - /// This constructor is correct. - #[ink(constructor)] - pub fn new1() -> Self { - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/fail/mapping-two-constructors-02.stderr b/linting/ui/fail/mapping-two-constructors-02.stderr deleted file mode 100644 index 8d0916f9888..00000000000 --- a/linting/ui/fail/mapping-two-constructors-02.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error: `#[ink(storage)]` on `MyContract` contains `ink_storage::Mapping` without initializing it in the contract constructor. - --> $DIR/mapping-two-constructors-02.rs:29:5 - | -LL | / #[derive(SpreadAllocate)] -LL | | pub struct MyContract { -LL | | balances: Mapping, -LL | | } - | |_____^ - | - = note: `#[deny(mapping_initialized)]` on by default -help: this field uses `ink_storage::Mapping` - --> $DIR/mapping-two-constructors-02.rs:31:9 - | -LL | balances: Mapping, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add an `ink_lang::utils::initialize_contract(…)` function in this constructor - | -LL ~ /// This constructor is **not** correct. -LL + /// It comes before the correct constructor. -LL + #[ink(constructor)] -LL + pub fn new2() -> Self { -LL + Self { -LL + balances: Default::default(), -LL + } -LL + } - | - -error: aborting due to previous error - diff --git a/linting/ui/pass/mapping-additional-logic-constructor.rs b/linting/ui/pass/mapping-additional-logic-constructor.rs deleted file mode 100644 index d5786452722..00000000000 --- a/linting/ui/pass/mapping-additional-logic-constructor.rs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - /// The `Mapping` must be recognized, even if it is imported - /// under a different name. - use ink_storage::{ - traits::SpreadAllocate, - Mapping as SomeOtherName, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: SomeOtherName, - } - - impl MyContract { - #[ink(constructor)] - pub fn new() -> Self { - let a = 1; - let b = 2; - assert_eq!(add(a, b), 3); - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } - - fn add(a: u32, b: u32) -> u32 { - a + b - } -} - -fn main() {} diff --git a/linting/ui/pass/mapping-dont-use-fully-qualified-path.rs b/linting/ui/pass/mapping-dont-use-fully-qualified-path.rs deleted file mode 100644 index c1245dc5275..00000000000 --- a/linting/ui/pass/mapping-dont-use-fully-qualified-path.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_lang::utils::initialize_contract as foo; - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - #[ink(constructor)] - pub fn new() -> Self { - foo(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/pass/mapping-one-constructor.rs b/linting/ui/pass/mapping-one-constructor.rs deleted file mode 100644 index 382238b4391..00000000000 --- a/linting/ui/pass/mapping-one-constructor.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - #[ink(constructor)] - pub fn new() -> Self { - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} diff --git a/linting/ui/pass/mapping-two-constructors.rs b/linting/ui/pass/mapping-two-constructors.rs deleted file mode 100644 index 4ee1dbe40e2..00000000000 --- a/linting/ui/pass/mapping-two-constructors.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018-2022 Parity Technologies (UK) Ltd. -// This file is part of cargo-contract. -// -// cargo-contract is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// cargo-contract is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with cargo-contract. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -mod my_contract { - use ink_storage::{ - traits::SpreadAllocate, - Mapping, - }; - - #[ink(storage)] - #[derive(SpreadAllocate)] - pub struct MyContract { - balances: Mapping, - } - - impl MyContract { - /// First constructor which uses the `new_init` helper function. - #[ink(constructor)] - pub fn new1() -> Self { - ink_lang::utils::initialize_contract(Self::new_init) - } - - /// Default initializes the contract. - fn new_init(&mut self) { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - self.balances.insert(&caller, &value); - } - - /// Second constructor which doesn't use the `new_init` helper function. - #[ink(constructor)] - pub fn new2() -> Self { - ink_lang::utils::initialize_contract(|contract: &mut Self| { - let caller = Self::env().caller(); - let value: Balance = Default::default(); - contract.balances.insert(&caller, &value); - }) - } - - /// Returns something. - #[ink(message)] - pub fn get(&self) { - // ... - } - } -} - -fn main() {} From cad9234c81d5dd624507e25827ceb109c7a1b08d Mon Sep 17 00:00:00 2001 From: xgreenx Date: Fri, 26 Aug 2022 11:39:06 +0100 Subject: [PATCH 33/40] Fix doc link --- crates/storage/src/traits/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index 121af32ac3b..852f3cc20bc 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -15,7 +15,7 @@ //! Traits and interfaces to operate with storage entities. //! //! Generally a type is said to be a storage entity if it implements the -//! [`Storable`] trait. This defines certain constants and routines in order +//! [`ink_primitives::traits::Storable`] 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. //! From 477e4cff8024bde3c5198e69dfbfabe0ee9da0c6 Mon Sep 17 00:00:00 2001 From: green Date: Tue, 30 Aug 2022 16:55:06 +0100 Subject: [PATCH 34/40] Fix mapping example --- crates/lang/tests/ui/contract/pass/example-erc20-works.rs | 2 +- examples/erc20/lib.rs | 2 +- examples/trait-erc20/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 f04b00d1dac..691c424b1cd 100644 --- a/crates/lang/tests/ui/contract/pass/example-erc20-works.rs +++ b/crates/lang/tests/ui/contract/pass/example-erc20-works.rs @@ -55,7 +55,7 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { - let mut balances: Mapping = Default::default(); + let mut balances = Mapping::default(); let caller = Self::env().caller(); balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { diff --git a/examples/erc20/lib.rs b/examples/erc20/lib.rs index 8c5e08e48c6..fc411ad57e3 100644 --- a/examples/erc20/lib.rs +++ b/examples/erc20/lib.rs @@ -57,7 +57,7 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { - let mut balances: Mapping = Default::default(); + let mut balances = Mapping::default(); let caller = Self::env().caller(); balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { diff --git a/examples/trait-erc20/lib.rs b/examples/trait-erc20/lib.rs index f656ace0e43..eaf9250e956 100644 --- a/examples/trait-erc20/lib.rs +++ b/examples/trait-erc20/lib.rs @@ -94,7 +94,7 @@ mod erc20 { /// Creates a new ERC-20 contract with the specified initial supply. #[ink(constructor)] pub fn new(total_supply: Balance) -> Self { - let mut balances: Mapping = Default::default(); + let mut balances = Mapping::default(); let caller = Self::env().caller(); balances.insert(&caller, &total_supply); Self::env().emit_event(Transfer { From 235d64968ec159cb884e4b4b9f2b3f34da345e86 Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 21:57:35 +0100 Subject: [PATCH 35/40] Applied suggestion. Removed `delegate-call` example with `OnCallInitializer` --- crates/lang/codegen/src/generator/dispatch.rs | 10 +- crates/lang/codegen/src/generator/metadata.rs | 2 +- .../codegen/src/generator/storage_item.rs | 2 +- crates/storage/src/lib.rs | 4 - crates/storage/src/pull_or_init.rs | 121 ------------------ crates/storage/src/traits/mod.rs | 1 - crates/storage/src/traits/storage.rs | 16 --- examples/upgradeable-contracts/README.md | 7 - .../delegate-calls/.gitignore | 9 -- .../delegate-calls/Cargo.toml | 35 ----- .../delegate-calls/README.md | 46 ------- .../delegate-calls/lib.rs | 113 ---------------- .../upgradeable-flipper/.gitignore | 9 -- .../upgradeable-flipper/Cargo.toml | 34 ----- .../delegate-calls/upgradeable-flipper/lib.rs | 71 ---------- 15 files changed, 8 insertions(+), 472 deletions(-) delete mode 100644 crates/storage/src/pull_or_init.rs delete mode 100644 examples/upgradeable-contracts/delegate-calls/.gitignore delete mode 100644 examples/upgradeable-contracts/delegate-calls/Cargo.toml delete mode 100644 examples/upgradeable-contracts/delegate-calls/README.md delete mode 100644 examples/upgradeable-contracts/delegate-calls/lib.rs delete mode 100644 examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/.gitignore delete mode 100644 examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml delete mode 100644 examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 6f7ea20ab06..553207e0651 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -810,12 +810,14 @@ impl Dispatch<'_> { fn execute_dispatchable( self ) -> ::core::result::Result<(), ::ink_lang::reflect::DispatchError> { + let key = <#storage_ident as ::ink_storage::traits::StorageKey>::KEY; let mut contract: ::core::mem::ManuallyDrop<#storage_ident> = ::core::mem::ManuallyDrop::new( - ::ink_storage::pull_or_init!( - #storage_ident, - <#storage_ident as ::ink_storage::traits::StorageKey>::KEY - ) + match ::ink_env::get_contract_storage(&key) { + Ok(Some(value)) => value, + Ok(None) => panic!("storage entry was empty"), + Err(_) => panic!("could not properly decode storage entry"), + } ); match self { diff --git a/crates/lang/codegen/src/generator/metadata.rs b/crates/lang/codegen/src/generator/metadata.rs index afa2431cbcd..ae43c0ec96d 100644 --- a/crates/lang/codegen/src/generator/metadata.rs +++ b/crates/lang/codegen/src/generator/metadata.rs @@ -69,7 +69,7 @@ impl Metadata<'_> { }; quote_spanned!(storage_span=> // Wrap the layout of the contract into the `RootLayout`, because - // contract storage key is reserved for all atomic fields + // contract storage key is reserved for all packed fields ::ink_metadata::layout::Layout::Root(::ink_metadata::layout::RootLayout::new( #layout_key, <#storage_ident as ::ink_storage::traits::StorageLayout>::layout( diff --git a/crates/lang/codegen/src/generator/storage_item.rs b/crates/lang/codegen/src/generator/storage_item.rs index c0c8b8d7480..0a183d69e1a 100644 --- a/crates/lang/codegen/src/generator/storage_item.rs +++ b/crates/lang/codegen/src/generator/storage_item.rs @@ -234,7 +234,7 @@ fn convert_into_storage_field( variant_name.as_str(), field_name.as_str(), ) - .unwrap(); + .expect("unable to compute the storage key for the field"); let mut new_field = field.clone(); let ty = field.ty.clone().to_token_stream(); diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index d6f960c0e57..b8b6076e74f 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -56,7 +56,3 @@ pub use self::lazy::{ Lazy, Mapping, }; - -#[macro_use] -#[doc(hidden)] -pub mod pull_or_init; diff --git a/crates/storage/src/pull_or_init.rs b/crates/storage/src/pull_or_init.rs deleted file mode 100644 index c587765a35d..00000000000 --- a/crates/storage/src/pull_or_init.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. - -//! The module helps during compilation time decide which pull mechanism to use. -//! If the type implements [`OnCallInitializer`](crate::traits::OnCallInitializer) trait, we will -//! try to pull storage first, on the failure will use `OnCallInitializer::initialize`. -//! -//! [`OnCallInitializer`](crate::traits::OnCallInitializer) allows initialize the -//! type on demand. For more information, check the documentation of the trait. - -use crate::traits::OnCallInitializer; -use ink_primitives::{ - traits::Storable, - Key, -}; - -/// Part of Autoref-Based Specialization. It is a wrapper around the type to support autoref -/// specialization. -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(value)) => value, - } - } -} - -/// Part of Autoref-Based Specialization. If the type doesn't implement `OnCallInitializer` trait -/// then the compiler will use this default implementation. -pub trait PullOrInitFallback { - #[allow(dead_code)] - fn pull_or_init(key: &Key) -> T { - match ink_env::get_contract_storage::(key) { - Ok(Some(value)) => value, - Ok(None) => panic!("storage entry was empty"), - Err(_) => panic!("could not properly decode storage entry"), - } - } -} -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::pull_or_init::PullOrInitFallback as _; - - $crate::pull_or_init::PullOrInit::<$T>::pull_or_init(&$key) - }}; -} - -#[cfg(test)] -mod tests { - use crate::traits::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; - ink_env::set_contract_storage(&KEY, &U32(456)); - 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; - ink_env::set_contract_storage(&KEY, &321); - let instance = pull_or_init!(u32, KEY); - assert_eq!(321, instance); - } -} diff --git a/crates/storage/src/traits/mod.rs b/crates/storage/src/traits/mod.rs index 852f3cc20bc..4295c353eda 100644 --- a/crates/storage/src/traits/mod.rs +++ b/crates/storage/src/traits/mod.rs @@ -44,7 +44,6 @@ pub use self::{ }, storage::{ AutoStorableHint, - OnCallInitializer, Packed, StorableHint, StorageKey, diff --git a/crates/storage/src/traits/storage.rs b/crates/storage/src/traits/storage.rs index f7ef7769ef7..6b95ca5f86c 100644 --- a/crates/storage/src/traits/storage.rs +++ b/crates/storage/src/traits/storage.rs @@ -70,19 +70,3 @@ pub trait AutoStorableHint { /// Storable type with storage key inside. type Type: Storable; } - -/// A trait to support initialization on the runtime if it cannot 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 { - /// A default instance of the contract is first created. The initialize method - /// is then called on that instance. There are no restrictions to what a developer - /// may do during the initialization phase, including doing nothing. - fn initialize(&mut self); -} diff --git a/examples/upgradeable-contracts/README.md b/examples/upgradeable-contracts/README.md index 4c8fc4fbdd1..b9f47b3ef73 100644 --- a/examples/upgradeable-contracts/README.md +++ b/examples/upgradeable-contracts/README.md @@ -14,13 +14,6 @@ more information on proxy patterns. * State is stored in the storage of the contract to which calls are forwarded. -## [`delegate-calls`](https://github.com/paritytech/ink/tree/master/examples/upgradeable-contracts/delegate-calls) - -* Executes any call that does not match a selector of itself with the code of another contract. -* The other contract does not need to be deployed on-chain. -* State is stored in the storage of the originally called contract. - - ## [`set-code-hash`](https://github.com/paritytech/ink/tree/master/examples/upgradeable-contracts/set-code-hash) * Updates the contract code using `set_code_hash`. diff --git a/examples/upgradeable-contracts/delegate-calls/.gitignore b/examples/upgradeable-contracts/delegate-calls/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/upgradeable-contracts/delegate-calls/Cargo.toml b/examples/upgradeable-contracts/delegate-calls/Cargo.toml deleted file mode 100644 index 28009b99182..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "delegate_calls" -version = "4.0.0-alpha.1" -authors = ["Parity Technologies "] -edition = "2021" -publish = false - -[dependencies] -ink_primitives = { path = "../../../crates/primitives", default-features = false } -ink_prelude = { path = "../../../crates/prelude", default-features = false } -ink_metadata = { path = "../../../crates/metadata", default-features = false, features = ["derive"], optional = true } -ink_env = { path = "../../../crates/env", default-features = false } -ink_storage = { path = "../../../crates/storage", default-features = false } -ink_lang = { path = "../../../crates/lang", default-features = false } - -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "delegate_calls" -path = "lib.rs" -crate-type = ["cdylib"] - -[features] -default = ["std"] -std = [ - "ink_primitives/std", - "ink_metadata/std", - "ink_env/std", - "ink_storage/std", - "ink_lang/std", - "scale/std", - "scale-info/std", -] -ink-as-dependency = [] diff --git a/examples/upgradeable-contracts/delegate-calls/README.md b/examples/upgradeable-contracts/delegate-calls/README.md deleted file mode 100644 index 860e596aea0..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Upgradeable Smart Contract - -The proxy smart contract delegates any call that does not match a -selector of itself to another, specified contract. - -The instantiator of the proxy contract on a blockchain can change -the code to which calls are forwarded. - -This allows building upgradeable contracts following [the proxy pattern](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies). - -In order to test it out you need to do the following: - -1. Build a contract containing some logic, e.g. our upgradeable flipper example: - ``` - cargo +nightly contract build --manifest-path=./upgradeable-flipper/Cargo.toml - ``` - You will receive the respective `upgradeable_flipper.contract` bundle in the - `./upgradeable-flipper/target/ink/` folder. - - In order to perform migrations and have proxy working for contracts with different - storage layouts, we implement the `OnCallInitializer` trait that acts as a - constructor during the execution of the contract. It initializes the contract if it was - not initialized. - -1. Build the proxy contract: - ``` - cargo +nightly contract build --manifest-path=./Cargo.toml - ``` - You will receive the respective `delegate_call.contract` bundle - in the `./target/ink/` folder. -1. Upload the `upgradeable_flipper.contract` to the chain. -1. Upload the `delegate_call.contract` to the chain. During instantiation - specify the just instantiated `upgradeable_flipper` contract as the `delegate_to` parameter. -1. Switch the metadata of the just instantiated `delegate_call` contract to the - metadata of the `upgradeable_flipper` contract. In the `polkadot-js` UI this can be - done this way: - 1. Click the icon left of the instantiated `delegate_call` contract to copy the - address of it into your clipboard. - 1. Click `Add an existing contract`, insert the just copied address, upload the - `upgradeable_flipper.contract` for the `Contract ABI`. -1. Now you are able to run the operations provided by the `upgradeable_flipper` smart - contract via the `delegate_call` contract. - -To change the address of the smart contract where calls are forwarded to you would -switch the metadata (i.e. the `Contract ABI`) back to the `delegate_call` contract -and then invoke the `change_delegate_code` message. diff --git a/examples/upgradeable-contracts/delegate-calls/lib.rs b/examples/upgradeable-contracts/delegate-calls/lib.rs deleted file mode 100644 index c71e8e92e14..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/lib.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! This example demonstrates how the Proxy pattern can be -//! implemented in ink! to have upgradeable functionality. -//! -//! What the contract does is: -//! -//! * Any call to this contract that does not match a selector -//! of it is delegates to a specified address. -//! * The instantiator of the contract can modify this specified -//! `forward_to` address at any point. -//! -//! User ---- tx ---> Proxy ---------> Implementation_v0 -//! | ------------> Implementation_v1 -//! | ------------> Implementation_v2 - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -pub mod upgradeable_contract { - use ink_env::call::DelegateCall; - use ink_primitives::{ - Key, - KeyComposer, - }; - use ink_storage::traits::{ - ManualKey, - StorageKey, - }; - - const PROXY_KEY: Key = KeyComposer::from_str("ProxyFields"); - - /// 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`. - /// - /// 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, - /// The `AccountId` of a privileged account that can update the - /// forwarding address. This address is set to the account that - /// instantiated this contract. - admin: AccountId, - } - - impl Proxy { - /// Instantiate this contract with an address of the `logic` contract. - /// - /// Sets the privileged account to the caller. Only this account may - /// later changed the `forward_to` address. - #[ink(constructor)] - pub fn new(forward_to: Hash) -> Self { - Self { - forward_to, - admin: Self::env().caller(), - } - } - - /// Changes the `Hash` of the contract where any call that does - /// not match a selector of this contract is delegated to. - #[ink(message)] - pub fn change_delegate_code(&mut self, new_code_hash: Hash) { - assert_eq!( - self.env().caller(), - self.admin, - "caller {:?} does not have sufficient permissions, only {:?} does", - self.env().caller(), - self.admin, - ); - self.forward_to = new_code_hash; - } - - /// Fallback message for a contract call that doesn't match any - /// of the other message selectors. Proxy contract delegates the execution - /// of that message to the `forward_to` contract with all input data. - /// - /// # Note: - /// - /// - We allow payable messages here and would forward any optionally supplied - /// value as well. - /// - If the self receiver were `forward(&mut self)` here, this would not - /// have any effect whatsoever on the contract we forward to. - #[ink(message, payable, selector = _)] - pub fn forward(&self) -> u32 { - ink_env::call::build_call::() - .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 - // input data can be forwarded to delegated contract to reduce the gas usage. - .set_forward_input(true) - // We don't plan to return back to that contract after execution, so we - // marked delegated call as "tail", to end the execution of the contract. - .set_tail_call(true), - ) - .fire() - .unwrap_or_else(|err| { - panic!( - "delegate call to {:?} failed due to {:?}", - self.forward_to, err - ) - }); - unreachable!( - "the forwarded call will never return since `tail_call` was set" - ); - } - } -} diff --git a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/.gitignore b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/.gitignore deleted file mode 100644 index bf910de10af..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Ignore build artifacts from the local tests sub-crate. -/target/ - -# Ignore backup files creates by cargo fmt. -**/*.rs.bk - -# Remove Cargo.lock when creating an executable, leave it for libraries -# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock -Cargo.lock \ No newline at end of file diff --git a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml deleted file mode 100644 index 4b235f956d3..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "upgradeable_flipper" -version = "4.0.0-alpha.1" -authors = ["Parity Technologies "] -edition = "2021" -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_storage = { path = "../../../../crates/storage", default-features = false } -ink_lang = { path = "../../../../crates/lang", default-features = false } - -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2", default-features = false, features = ["derive"], optional = true } - -[lib] -name = "upgradeable_flipper" -path = "lib.rs" -crate-type = ["cdylib"] - -[features] -default = ["std"] -std = [ - "ink_primitives/std", - "ink_metadata/std", - "ink_env/std", - "ink_storage/std", - "ink_lang/std", - "scale/std", - "scale-info/std", -] -ink-as-dependency = [] diff --git a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs b/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs deleted file mode 100644 index fc23928c5cc..00000000000 --- a/examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/lib.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! This is an example of an upgradable `Flipper`, that can be deployed by the developer -//! and used with `Proxy` from the `upgradeable_contract` crate. -//! The calls from the `Proxy` contract can be delegated to that contract. - -#![cfg_attr(not(feature = "std"), no_std)] - -use ink_lang as ink; - -#[ink::contract] -pub mod flipper { - 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 { - 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: init_value } - } - - /// Flips the current value of the Flipper's boolean. - #[ink(message)] - pub fn flip(&mut self) { - self.value = !self.value; - } - - /// Returns the current value of the Flipper's boolean. - #[ink(message)] - pub fn get(&self) -> bool { - self.value - } - } - - #[cfg(test)] - mod tests { - use super::*; - use ink_lang as ink; - - #[ink::test] - fn default_works() { - let flipper = Flipper::default(); - assert!(!flipper.get()); - } - - #[ink::test] - fn it_works() { - let mut flipper = Flipper::new(false); - assert!(!flipper.get()); - flipper.flip(); - assert!(flipper.get()); - } - } -} From 8b9ccbcb6a73de3ee775bfb04eeb5f9e3b56f2f2 Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 22:08:22 +0100 Subject: [PATCH 36/40] Removed `delegate-calls` from the CI. Replaced it with `set-code-hash` --- .github/workflows/examples.yml | 8 ++++---- .gitlab-ci.yml | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 0e242c5f3e7..ff30348a74d 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -32,7 +32,7 @@ jobs: - test runs-on: ${{ matrix.platform }} env: - UPGRADEABLE_CONTRACTS: "forward-calls delegate-calls" + UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash" DELEGATOR_SUBCONTRACTS: "accumulator adder subber" RUST_BACKTRACE: full steps: @@ -90,12 +90,12 @@ jobs: echo "Processing delegator contract: $contract"; cargo contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; } - $upgradeable_contracts = "forward-calls","delegate-calls" + $upgradeable_contracts = "forward-calls","set-code-hash" foreach ($contract in $upgradeable_contracts) { echo "Processing upgradeable contract: $contract"; cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/${contract}/Cargo.toml; } - cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; foreach ($example in Get-ChildItem -Directory "examples\*") { if ($example -Match 'upgradeable-contracts') { continue } echo "Processing example: $example"; @@ -114,7 +114,7 @@ jobs: echo "Processing upgradeable contract: $contract"; cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/$contract/Cargo.toml; done - cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; for example in examples/*/; do if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi; echo "Processing example: $example"; diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 308502e275c..b83096177b0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -33,7 +33,7 @@ variables: ALSO_WASM_CRATES: "env storage storage/derive allocator prelude primitives lang lang/macro lang/ir" ALL_CRATES: "${PURELY_STD_CRATES} ${ALSO_WASM_CRATES}" DELEGATOR_SUBCONTRACTS: "accumulator adder subber" - UPGRADEABLE_CONTRACTS: "forward-calls delegate-calls" + UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash" workflow: rules: @@ -122,7 +122,7 @@ examples-fmt: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo fmt --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml -- --check; done - - cargo fmt --verbose --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml -- --check + - cargo fmt --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml -- --check allow_failure: true clippy-std: @@ -159,7 +159,7 @@ examples-clippy-std: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo clippy --verbose --all-targets --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml -- -D warnings; done - - cargo clippy --verbose --all-targets --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml -- -D warnings; + - cargo clippy --verbose --all-targets --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml -- -D warnings; allow_failure: true examples-clippy-wasm: @@ -177,7 +177,7 @@ examples-clippy-wasm: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo clippy --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings; done - - cargo clippy --verbose --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings; + - cargo clippy --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml --no-default-features --target wasm32-unknown-unknown -- -D warnings; allow_failure: true @@ -347,7 +347,7 @@ examples-test: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo test --verbose --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml; done - - cargo test --verbose --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml; + - cargo test --verbose --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; examples-contract-build: stage: examples @@ -367,7 +367,7 @@ examples-contract-build: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo +stable contract build --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml; done - - cargo +stable contract build --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml + - cargo +stable contract build --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml examples-docs: stage: examples @@ -390,7 +390,7 @@ examples-docs: - for contract in ${UPGRADEABLE_CONTRACTS}; do cargo doc --manifest-path ./examples/upgradeable-contracts/${contract}/Cargo.toml --document-private-items --verbose --no-deps; done - - cargo doc --manifest-path ./examples/upgradeable-contracts/delegate-calls/upgradeable-flipper/Cargo.toml --document-private-items --verbose --no-deps + - cargo doc --manifest-path ./examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml --document-private-items --verbose --no-deps #### stage: ink-waterfall From be1563efdd2ca1d5a41ceff19107734eb5ebf06f Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 22:43:02 +0100 Subject: [PATCH 37/40] fix test --- crates/lang/codegen/src/generator/dispatch.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 553207e0651..74615d72b5a 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -814,9 +814,11 @@ impl Dispatch<'_> { let mut contract: ::core::mem::ManuallyDrop<#storage_ident> = ::core::mem::ManuallyDrop::new( match ::ink_env::get_contract_storage(&key) { - Ok(Some(value)) => value, - Ok(None) => panic!("storage entry was empty"), - Err(_) => panic!("could not properly decode storage entry"), + ::core::result::Result::Ok(Some(value)) => value, + ::core::result::Result::Ok(None) => ::core::panic!("storage entry was empty"), + ::core::result::Result::Err(_) => ::core::panic!( + "could not properly decode storage entry" + ), } ); From 300b1399cf2eceedc46790cd199dfb5b8bb23f1f Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 22:53:08 +0100 Subject: [PATCH 38/40] fix test --- crates/lang/codegen/src/generator/dispatch.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/lang/codegen/src/generator/dispatch.rs b/crates/lang/codegen/src/generator/dispatch.rs index 74615d72b5a..a86dae87bc6 100644 --- a/crates/lang/codegen/src/generator/dispatch.rs +++ b/crates/lang/codegen/src/generator/dispatch.rs @@ -814,11 +814,13 @@ impl Dispatch<'_> { let mut contract: ::core::mem::ManuallyDrop<#storage_ident> = ::core::mem::ManuallyDrop::new( match ::ink_env::get_contract_storage(&key) { - ::core::result::Result::Ok(Some(value)) => value, - ::core::result::Result::Ok(None) => ::core::panic!("storage entry was empty"), - ::core::result::Result::Err(_) => ::core::panic!( - "could not properly decode storage entry" - ), + ::core::result::Result::Ok(::core::option::Option::Some(value)) => value, + ::core::result::Result::Ok(::core::option::Option::None) => { + ::core::panic!("storage entry was empty") + }, + ::core::result::Result::Err(_) => { + ::core::panic!("could not properly decode storage entry") + }, } ); From 7b8733dd97daa757f48be273bc63a140e0d84955 Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 23:18:22 +0100 Subject: [PATCH 39/40] Fix CI to use stable for contract build --- .github/workflows/examples.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index ff30348a74d..d119217164b 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -88,18 +88,18 @@ jobs: $delegator_subcontracts = "accumulator","adder","subber" foreach ($contract in $delegator_subcontracts) { echo "Processing delegator contract: $contract"; - cargo contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; } $upgradeable_contracts = "forward-calls","set-code-hash" foreach ($contract in $upgradeable_contracts) { echo "Processing upgradeable contract: $contract"; - cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/${contract}/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/${contract}/Cargo.toml; } - cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; foreach ($example in Get-ChildItem -Directory "examples\*") { if ($example -Match 'upgradeable-contracts') { continue } echo "Processing example: $example"; - cargo contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; cargo clean --manifest-path=$example/Cargo.toml; } @@ -108,15 +108,15 @@ jobs: run: | for contract in ${DELEGATOR_SUBCONTRACTS}; do echo "Processing delegator contract: $contract"; - cargo contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; done for contract in ${UPGRADEABLE_CONTRACTS}; do echo "Processing upgradeable contract: $contract"; - cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/$contract/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/$contract/Cargo.toml; done - cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; for example in examples/*/; do if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi; echo "Processing example: $example"; - cargo contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; + cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; done From 3e3fbd748c9a8cc89e9d7bc6537ca0030dc63633 Mon Sep 17 00:00:00 2001 From: green Date: Wed, 31 Aug 2022 23:38:00 +0100 Subject: [PATCH 40/40] Fix CI to use stable for examples --- .github/workflows/examples.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index d119217164b..b88177bd8c1 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -26,7 +26,7 @@ jobs: # Once this is resolved we should re-enable building the examples on windows # - windows-latest toolchain: - - nightly + - stable job: - build - test @@ -35,6 +35,11 @@ jobs: UPGRADEABLE_CONTRACTS: "forward-calls set-code-hash" DELEGATOR_SUBCONTRACTS: "accumulator adder subber" RUST_BACKTRACE: full + # We need to enable `RUSTC_BOOTSTRAP` so that the nightly ink! + # features still work on stable. This is done automatically by + # `cargo-contract`, but in our CI here we also use e.g. + # `cargo check` directly. + RUSTC_BOOTSTRAP: "1" steps: - uses: actions/setup-node@v3 @@ -88,18 +93,18 @@ jobs: $delegator_subcontracts = "accumulator","adder","subber" foreach ($contract in $delegator_subcontracts) { echo "Processing delegator contract: $contract"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; } $upgradeable_contracts = "forward-calls","set-code-hash" foreach ($contract in $upgradeable_contracts) { echo "Processing upgradeable contract: $contract"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/${contract}/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/${contract}/Cargo.toml; } - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; foreach ($example in Get-ChildItem -Directory "examples\*") { if ($example -Match 'upgradeable-contracts') { continue } echo "Processing example: $example"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; cargo clean --manifest-path=$example/Cargo.toml; } @@ -108,15 +113,15 @@ jobs: run: | for contract in ${DELEGATOR_SUBCONTRACTS}; do echo "Processing delegator contract: $contract"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path examples/delegator/${contract}/Cargo.toml; done for contract in ${UPGRADEABLE_CONTRACTS}; do echo "Processing upgradeable contract: $contract"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/$contract/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/$contract/Cargo.toml; done - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path=examples/upgradeable-contracts/set-code-hash/updated-incrementer/Cargo.toml; for example in examples/*/; do if [ "$example" = "examples/upgradeable-contracts/" ]; then continue; fi; echo "Processing example: $example"; - cargo +stable contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; + cargo contract ${{ matrix.job }} --verbose --manifest-path=$example/Cargo.toml; done