From 8a0bc02ab49e3ef513c1f5f7dc98fd8d1fc83805 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:11:24 +0200 Subject: [PATCH 001/114] create mock and testing --- runtime/src/precompiles/mock.rs | 153 ++++++++++++++++++ .../{precompiles.rs => precompiles/mod.rs} | 6 + runtime/src/precompiles/test.rs | 8 + 3 files changed, 167 insertions(+) create mode 100644 runtime/src/precompiles/mock.rs rename runtime/src/{precompiles.rs => precompiles/mod.rs} (97%) create mode 100644 runtime/src/precompiles/test.rs diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs new file mode 100644 index 00000000..8f4c28e1 --- /dev/null +++ b/runtime/src/precompiles/mock.rs @@ -0,0 +1,153 @@ +use frame_support::{ + construct_runtime, parameter_types, sp_io, + traits::{ConstU64, FindAuthor}, + weights::Weight, +}; +use pallet_evm::{Config, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, FixedGasWeightMapping, IdentityAddressMapping, SubstrateBlockHashMapping, runner}; +use sp_core::{H160, H256, U256}; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, ConsensusEngineId, +}; +use sp_std::{boxed::Box, prelude::*, str::FromStr}; + +use super::FrontierPrecompiles; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + Timestamp: pallet_timestamp, + LivingassetsOwnership: pallet_living_assets_ownership, + EVM: pallet_evm, + } +); + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = H160; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Balance = u64; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type ReserveIdentifier = (); + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxLocks = (); + type MaxReserves = (); + type MaxHolds = (); + type MaxFreezes = (); +} + +parameter_types! { + pub const MinimumPeriod: u64 = 1000; +} + +impl pallet_timestamp::Config for Runtime { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} + +impl pallet_living_assets_ownership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u64; +} + +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> (U256, Weight) { + // Return some meaningful gas price and weight + (1_000_000_000u128.into(), Weight::from_parts(7u64, 0)) + } +} +pub struct FindAuthorTruncated; +impl FindAuthor for FindAuthorTruncated { + fn find_author<'a, I>(_digests: I) -> Option + where + I: 'a + IntoIterator, + { + Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) + } +} + +const BLOCK_GAS_LIMIT: u64 = 150_000_000; +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; + +parameter_types! { + pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); + pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); + pub PrecompilesValue: FrontierPrecompiles = FrontierPrecompiles::<_>::new(); +} + +impl pallet_evm::Config for Runtime { + type FeeCalculator = FixedGasPrice; + type GasWeightMapping = FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = SubstrateBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = FrontierPrecompiles; + type PrecompilesValue = PrecompilesValue; + type ChainId = (); + type BlockGasLimit = BlockGasLimit; + type Runner = runner::stack::Runner; + type OnChargeTransaction = (); + type OnCreate = (); + type FindAuthor = FindAuthorTruncated; + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; + type Timestamp = Timestamp; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + frame_system::GenesisConfig::default() + .build_storage::() + .unwrap() + .into() +} diff --git a/runtime/src/precompiles.rs b/runtime/src/precompiles/mod.rs similarity index 97% rename from runtime/src/precompiles.rs rename to runtime/src/precompiles/mod.rs index 8aaa6e01..41683254 100644 --- a/runtime/src/precompiles.rs +++ b/runtime/src/precompiles/mod.rs @@ -65,3 +65,9 @@ where fn hash(a: u64) -> H160 { H160::from_low_u64_be(a) } + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod test; diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs new file mode 100644 index 00000000..1ee73185 --- /dev/null +++ b/runtime/src/precompiles/test.rs @@ -0,0 +1,8 @@ +use super::mock::*; + +#[test] +fn check_precompiled() { + new_test_ext().execute_with(|| { + // PrecompilesValue::is_precompile(hash(1), 0); + }) +} From 0fe9a63087649c5d5edb224e5c3cda449ce992fc Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:40:49 +0200 Subject: [PATCH 002/114] first test --- runtime/src/precompiles/mock.rs | 14 +++++++++----- runtime/src/precompiles/test.rs | 14 +++++++++++--- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs index 8f4c28e1..7535a986 100644 --- a/runtime/src/precompiles/mock.rs +++ b/runtime/src/precompiles/mock.rs @@ -3,11 +3,15 @@ use frame_support::{ traits::{ConstU64, FindAuthor}, weights::Weight, }; -use pallet_evm::{Config, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, FixedGasWeightMapping, IdentityAddressMapping, SubstrateBlockHashMapping, runner}; +use pallet_evm::{ + runner, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, FixedGasWeightMapping, + IdentityAddressMapping, SubstrateBlockHashMapping, +}; use sp_core::{H160, H256, U256}; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, IdentityLookup}, ConsensusEngineId, + traits::{BlakeTwo256, IdentityLookup}, + ConsensusEngineId, }; use sp_std::{boxed::Box, prelude::*, str::FromStr}; @@ -26,7 +30,7 @@ construct_runtime!( System: frame_system, Balances: pallet_balances, Timestamp: pallet_timestamp, - LivingassetsOwnership: pallet_living_assets_ownership, + LivingassetsOwnership: pallet_living_assets_ownership, EVM: pallet_evm, } ); @@ -90,8 +94,8 @@ impl pallet_timestamp::Config for Runtime { } impl pallet_living_assets_ownership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type CollectionId = u64; + type RuntimeEvent = RuntimeEvent; + type CollectionId = u64; } pub struct FixedGasPrice; diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 1ee73185..ade8b072 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -1,8 +1,16 @@ -use super::mock::*; +use super::{hash, mock::*, FrontierPrecompiles}; +use pallet_evm::{IsPrecompileResult, PrecompileSet}; #[test] fn check_precompiled() { new_test_ext().execute_with(|| { - // PrecompilesValue::is_precompile(hash(1), 0); - }) + let p = FrontierPrecompiles::::new(); + let result = p.is_precompile(hash(4), 0); + if let IsPrecompileResult::Answer { is_precompile, extra_cost } = result { + assert_eq!(is_precompile, true); + assert_eq!(extra_cost, 0); + } else { + panic!("Unexpected result variant"); + } + }) } From 7ebabef2ec68f5360d561c0d48b3b164a4f399ef Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:52:51 +0200 Subject: [PATCH 003/114] testing ethereum reserved addresses --- runtime/src/precompiles/mock.rs | 10 +--------- runtime/src/precompiles/test.rs | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs index 7535a986..d8b58b46 100644 --- a/runtime/src/precompiles/mock.rs +++ b/runtime/src/precompiles/mock.rs @@ -1,5 +1,5 @@ use frame_support::{ - construct_runtime, parameter_types, sp_io, + construct_runtime, parameter_types, traits::{ConstU64, FindAuthor}, weights::Weight, }; @@ -147,11 +147,3 @@ impl pallet_evm::Config for Runtime { type Timestamp = Timestamp; type WeightInfo = (); } - -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default() - .build_storage::() - .unwrap() - .into() -} diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index ade8b072..17489f1e 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -1,16 +1,27 @@ use super::{hash, mock::*, FrontierPrecompiles}; use pallet_evm::{IsPrecompileResult, PrecompileSet}; +use sp_core::H160; + +fn is_precompile(address: H160) -> bool { + let p = FrontierPrecompiles::::new(); + let result = p.is_precompile(address, 0); + if let IsPrecompileResult::Answer { is_precompile, extra_cost } = result { + is_precompile + } else { + panic!("Unexpected result variant"); + } +} + +#[test] +fn null_address_is_not_precompile() { + assert!(!is_precompile(H160::zero())); +} #[test] -fn check_precompiled() { - new_test_ext().execute_with(|| { - let p = FrontierPrecompiles::::new(); - let result = p.is_precompile(hash(4), 0); - if let IsPrecompileResult::Answer { is_precompile, extra_cost } = result { - assert_eq!(is_precompile, true); - assert_eq!(extra_cost, 0); - } else { - panic!("Unexpected result variant"); - } - }) +fn ethrerum_precompiled_reserved_addresses_are_precompiled() { + assert!(is_precompile(hash(1))); + assert!(is_precompile(hash(2))); + assert!(is_precompile(hash(3))); + assert!(is_precompile(hash(4))); + assert!(is_precompile(hash(5))); } From e964668a1a6122e9c36e7b2578cd6b4b952e4a8e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:55:24 +0200 Subject: [PATCH 004/114] refactoring --- runtime/src/precompiles/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 17489f1e..291ae043 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -5,7 +5,7 @@ use sp_core::H160; fn is_precompile(address: H160) -> bool { let p = FrontierPrecompiles::::new(); let result = p.is_precompile(address, 0); - if let IsPrecompileResult::Answer { is_precompile, extra_cost } = result { + if let IsPrecompileResult::Answer { is_precompile, extra_cost: _ } = result { is_precompile } else { panic!("Unexpected result variant"); From aba874eed23e098d8d513657bf80e3fc49295226 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:57:47 +0200 Subject: [PATCH 005/114] refactoring --- runtime/src/precompiles/test.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 291ae043..943bde3f 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -2,26 +2,24 @@ use super::{hash, mock::*, FrontierPrecompiles}; use pallet_evm::{IsPrecompileResult, PrecompileSet}; use sp_core::H160; -fn is_precompile(address: H160) -> bool { +fn is_precompile(address: H160) -> Result { let p = FrontierPrecompiles::::new(); - let result = p.is_precompile(address, 0); - if let IsPrecompileResult::Answer { is_precompile, extra_cost: _ } = result { - is_precompile - } else { - panic!("Unexpected result variant"); + match p.is_precompile(address, 0) { + IsPrecompileResult::Answer { is_precompile, extra_cost: _ } => Ok(is_precompile), + _ => Err("Unexpected result variant"), } } #[test] fn null_address_is_not_precompile() { - assert!(!is_precompile(H160::zero())); + assert!(!is_precompile(H160::zero()).unwrap()); } #[test] fn ethrerum_precompiled_reserved_addresses_are_precompiled() { - assert!(is_precompile(hash(1))); - assert!(is_precompile(hash(2))); - assert!(is_precompile(hash(3))); - assert!(is_precompile(hash(4))); - assert!(is_precompile(hash(5))); + assert!(is_precompile(hash(1)).unwrap()); + assert!(is_precompile(hash(2)).unwrap()); + assert!(is_precompile(hash(3)).unwrap()); + assert!(is_precompile(hash(4)).unwrap()); + assert!(is_precompile(hash(5)).unwrap()); } From 80dd172dc442ab0601936be535679dee4b93bcba Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 11:59:34 +0200 Subject: [PATCH 006/114] refactoring tests --- runtime/src/precompiles/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 943bde3f..9df6d657 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -16,7 +16,7 @@ fn null_address_is_not_precompile() { } #[test] -fn ethrerum_precompiled_reserved_addresses_are_precompiled() { +fn ethereum_precompiled_addresses_are_precompile() { assert!(is_precompile(hash(1)).unwrap()); assert!(is_precompile(hash(2)).unwrap()); assert!(is_precompile(hash(3)).unwrap()); From 9604f6c92d88bcec583db83e7929f5993c46362f Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 1 Aug 2023 12:42:20 +0200 Subject: [PATCH 007/114] fmt --- runtime/src/precompiles/test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 9df6d657..f9c475da 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -12,14 +12,14 @@ fn is_precompile(address: H160) -> Result { #[test] fn null_address_is_not_precompile() { - assert!(!is_precompile(H160::zero()).unwrap()); + assert!(!is_precompile(H160::zero()).unwrap()); } #[test] fn ethereum_precompiled_addresses_are_precompile() { - assert!(is_precompile(hash(1)).unwrap()); - assert!(is_precompile(hash(2)).unwrap()); - assert!(is_precompile(hash(3)).unwrap()); - assert!(is_precompile(hash(4)).unwrap()); - assert!(is_precompile(hash(5)).unwrap()); + assert!(is_precompile(hash(1)).unwrap()); + assert!(is_precompile(hash(2)).unwrap()); + assert!(is_precompile(hash(3)).unwrap()); + assert!(is_precompile(hash(4)).unwrap()); + assert!(is_precompile(hash(5)).unwrap()); } From 390359baf2eaa196071c84bcbbb0870412cd0465 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 3 Aug 2023 18:02:43 +0200 Subject: [PATCH 008/114] erc721 starting point --- Cargo.lock | 22 ++++++++++ precompile/erc721/Cargo.toml | 55 +++++++++++++++++++++++++ precompile/erc721/contracts/IERC721.sol | 10 +++++ precompile/erc721/src/lib.rs | 49 ++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 precompile/erc721/Cargo.toml create mode 100644 precompile/erc721/contracts/IERC721.sol create mode 100644 precompile/erc721/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index afac0916..5c5dec34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8914,6 +8914,28 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precompile-erc721" +version = "0.0.1" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "hex", + "num_enum 0.5.11", + "pallet-evm", + "pallet-evm-test-vector-support", + "pallet-living-assets-ownership", + "parity-scale-codec", + "precompile-utils", + "precompile-utils-macro", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "precompile-utils" version = "0.4.3" diff --git a/precompile/erc721/Cargo.toml b/precompile/erc721/Cargo.toml new file mode 100644 index 00000000..f5d53602 --- /dev/null +++ b/precompile/erc721/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "precompile-erc721" +version = "0.0.1" +edition = "2021" + +[dependencies] +parity-scale-codec = { workspace = true, features = [ + "derive", +] } +scale-info = { workspace = true, features = [ + "derive", +] } + +# Frontier +fp-evm = { workspace = true } +pallet-evm = { workspace = true } + +# Substrate +frame-support = { workspace = true } +sp-arithmetic = { workspace = true } +sp-core = { workspace = true } +sp-runtime = { workspace = true } +sp-std = { workspace = true } + +# Local pallet +pallet-living-assets-ownership = { workspace = true } + +# Utils +precompile-utils = { workspace = true } +precompile-utils-macro = { workspace = true } + +num_enum = { workspace = true } + +[dev-dependencies] +pallet-evm-test-vector-support = { workspace = true } +evm = { workspace = true } +hex = { version = "0.4.3" } + +[features] +default = ["std"] +std = [ + # Frontier + "fp-evm/std", + "pallet-evm/std", + "sp-core/std", + "sp-runtime/std", + "sp-std/std", + "pallet-living-assets-ownership/std", + "num_enum/std", + "frame-support/std", + "sp-arithmetic/std", + "precompile-utils/std", + "parity-scale-codec/std", + "scale-info/std", +] diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol new file mode 100644 index 00000000..a0c3d8ca --- /dev/null +++ b/precompile/erc721/contracts/IERC721.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// derived from OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol) +pragma solidity >=0.8.3; + +interface IERC721 { + /** + * @dev See {IERC721Metadata-tokenURI}. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs new file mode 100644 index 00000000..11210ffa --- /dev/null +++ b/precompile/erc721/src/lib.rs @@ -0,0 +1,49 @@ +//! Living Assets precompile module. + +#![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(test, feature(assert_matches))] +use fp_evm::{ + Precompile, PrecompileHandle, PrecompileOutput, +}; +use parity_scale_codec::Encode; +use precompile_utils::{EvmResult, FunctionModifier, PrecompileHandleExt}; + +use sp_std::{fmt::Debug, marker::PhantomData}; + +#[precompile_utils_macro::generate_function_selector] +#[derive(Debug, PartialEq)] +pub enum Action { + /// Get tocken URI + TockenURI = "tokenURI(uint256)", +} + +/// Wrapper for the precompile function. +pub struct CollectionManagerPrecompile( + PhantomData<(AddressMapping, AccountId)>, +) +where + AddressMapping: pallet_evm::AddressMapping, + AccountId: Encode + Debug; + +impl Precompile + for CollectionManagerPrecompile +where + AddressMapping: pallet_evm::AddressMapping, + AccountId: Encode + Debug, +{ + fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { + let selector = handle.read_selector()?; + + handle.check_function_modifier(match selector { + Action::TockenURI => FunctionModifier::NonPayable, + })?; + + match selector { + Action::TockenURI => { + todo!() + }, + } + } +} + + From 5dafd9f7fbe3147469110a5190bd3eb3e3e2bf07 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 3 Aug 2023 18:06:30 +0200 Subject: [PATCH 009/114] fmt --- precompile/erc721/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 11210ffa..c29fabee 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -2,9 +2,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(test, feature(assert_matches))] -use fp_evm::{ - Precompile, PrecompileHandle, PrecompileOutput, -}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use parity_scale_codec::Encode; use precompile_utils::{EvmResult, FunctionModifier, PrecompileHandleExt}; @@ -45,5 +43,3 @@ where } } } - - From e06adce4e29da42cf9378d55cdb7766e08b018b2 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:02:00 +0200 Subject: [PATCH 010/114] compiling --- Cargo.lock | 1 - precompile/erc721/Cargo.toml | 1 - precompile/erc721/contracts/IERC721.sol | 2 ++ precompile/erc721/src/lib.rs | 7 ++++++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2392e46..222f2646 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9406,7 +9406,6 @@ dependencies = [ "hex", "num_enum 0.5.11", "pallet-evm", - "pallet-evm-test-vector-support", "pallet-living-assets-ownership", "parity-scale-codec", "precompile-utils", diff --git a/precompile/erc721/Cargo.toml b/precompile/erc721/Cargo.toml index f5d53602..58f587d5 100644 --- a/precompile/erc721/Cargo.toml +++ b/precompile/erc721/Cargo.toml @@ -32,7 +32,6 @@ precompile-utils-macro = { workspace = true } num_enum = { workspace = true } [dev-dependencies] -pallet-evm-test-vector-support = { workspace = true } evm = { workspace = true } hex = { version = "0.4.3" } diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol index a0c3d8ca..2325486a 100644 --- a/precompile/erc721/contracts/IERC721.sol +++ b/precompile/erc721/contracts/IERC721.sol @@ -7,4 +7,6 @@ interface IERC721 { * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) external view returns (string memory); + + function ownerOf(uint256 _tokenId) external view returns (address); } diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index c29fabee..7e551b1c 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,7 +1,6 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(test, feature(assert_matches))] use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use parity_scale_codec::Encode; use precompile_utils::{EvmResult, FunctionModifier, PrecompileHandleExt}; @@ -13,6 +12,8 @@ use sp_std::{fmt::Debug, marker::PhantomData}; pub enum Action { /// Get tocken URI TockenURI = "tokenURI(uint256)", + /// Owner of + OwnerOf = "ownerOf(uint256)", } /// Wrapper for the precompile function. @@ -34,12 +35,16 @@ where handle.check_function_modifier(match selector { Action::TockenURI => FunctionModifier::NonPayable, + Action::OwnerOf => FunctionModifier::NonPayable, })?; match selector { Action::TockenURI => { todo!() }, + Action::OwnerOf => { + todo!() + }, } } } From 3869690b963e8e17d0800e6d12fe9d96a95b5a33 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:06:38 +0200 Subject: [PATCH 011/114] check selectors --- precompile/erc721/src/lib.rs | 3 +++ precompile/erc721/src/tests.rs | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 precompile/erc721/src/tests.rs diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 7e551b1c..3436d617 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -48,3 +48,6 @@ where } } } + +#[cfg(test)] +mod tests; diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs new file mode 100644 index 00000000..2ac018eb --- /dev/null +++ b/precompile/erc721/src/tests.rs @@ -0,0 +1,7 @@ +use super::*; + +#[test] +fn check_selectors() { + assert_eq!(Action::OwnerOf as u32, 0x6352211E); + assert_eq!(Action::TockenURI as u32, 0xC87B56DD); +} From d514f880923c3900ef0afebdd53f277300243c83 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:09:19 +0200 Subject: [PATCH 012/114] create trait Erc721 --- pallets/living-assets-ownership/Cargo.toml | 1 + pallets/living-assets-ownership/src/traits.rs | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index d7310f6e..47227a05 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -20,6 +20,7 @@ frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } sp-arithmetic = { workspace = true } +sp-core = { workspace = true } [dev-dependencies] serde = { workspace = true } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 0ad629f3..a9975100 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,3 +1,5 @@ +use sp_core::{U256, H160}; + use crate::CollectionId; /// The `CollectionManager` trait provides an interface for managing collections in a @@ -25,3 +27,7 @@ pub trait CollectionManager { /// Create collection fn create_collection(owner: AccountId) -> Result; } + +pub trait Erc721 { + fn owner_of(asset_id: U256) -> Option; +} \ No newline at end of file From 2efff9286becb8310896dd8a850bdab08340297f Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:14:54 +0200 Subject: [PATCH 013/114] Erc721Precompile --- precompile/erc721/src/lib.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 3436d617..8f4a066c 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -17,15 +17,12 @@ pub enum Action { } /// Wrapper for the precompile function. -pub struct CollectionManagerPrecompile( - PhantomData<(AddressMapping, AccountId)>, -) +pub struct Erc721Precompile(PhantomData<(AddressMapping, AccountId)>) where AddressMapping: pallet_evm::AddressMapping, AccountId: Encode + Debug; -impl Precompile - for CollectionManagerPrecompile +impl Precompile for Erc721Precompile where AddressMapping: pallet_evm::AddressMapping, AccountId: Encode + Debug, From 0357938ccdc71ffb41fe8f19f5b5d78404cd841d Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:33:45 +0200 Subject: [PATCH 014/114] set mocks --- precompile/erc721/src/lib.rs | 12 +- precompile/erc721/src/tests.rs | 204 ++++++++++++++++++++++++++ precompile/living-assets/src/tests.rs | 1 - 3 files changed, 213 insertions(+), 4 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 8f4a066c..01560caa 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -2,6 +2,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; +use pallet_living_assets_ownership::traits::Erc721; use parity_scale_codec::Encode; use precompile_utils::{EvmResult, FunctionModifier, PrecompileHandleExt}; @@ -17,15 +18,20 @@ pub enum Action { } /// Wrapper for the precompile function. -pub struct Erc721Precompile(PhantomData<(AddressMapping, AccountId)>) +pub struct Erc721Precompile( + PhantomData<(AddressMapping, AccountId, AssetManager)>, +) where AddressMapping: pallet_evm::AddressMapping, - AccountId: Encode + Debug; + AccountId: Encode + Debug, + AssetManager: Erc721; -impl Precompile for Erc721Precompile +impl Precompile + for Erc721Precompile where AddressMapping: pallet_evm::AddressMapping, AccountId: Encode + Debug, + AssetManager: Erc721, { fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { let selector = handle.read_selector()?; diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 2ac018eb..640e8c46 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,7 +1,211 @@ use super::*; +use helpers::*; +use sp_core::{H160, U256}; + +type AccountId = H160; +type AddressMapping = pallet_evm::IdentityAddressMapping; #[test] fn check_selectors() { assert_eq!(Action::OwnerOf as u32, 0x6352211E); assert_eq!(Action::TockenURI as u32, 0xC87B56DD); } + +#[test] +fn create_collection_should_return_address() { + impl_precompile_mock_simple!(Mock, None); + + let owner_of_1234 = "6352211e00000000000000000000000000000000000000000000000000000000000004d2"; + let mut handle = create_mock_handle_from_input(owner_of_1234); + let result = Mock::execute(&mut handle); + assert!(result.is_ok()); +} + +mod helpers { + use evm::{Context, ExitError, ExitReason, Transfer}; + use fp_evm::{Log, PrecompileHandle}; + use sp_core::{H160, H256}; + + /// Macro to define a precompile mock with custom closures for testing. + /// + /// This macro creates mock implementations of the `Erc721` trait, + /// allowing you to test how your code interacts with the precompiled contracts. + /// You can define custom closures for the create_collection and owner_of_collection functions. + /// + /// # Arguments + /// + /// * `$name`: An identifier to name the precompile mock type. + /// * `$create_collection_result`: A closure that takes `collection_id` and `who` and returns a `DispatchResult`. + /// * `$owner_of_collection_result`: A closure that takes `collection_id` and returns an `Option`. + /// + /// # Example + /// + /// ``` + /// impl_precompile_mock!( + /// MyMock, + /// |who| { Ok(0) }, + /// |collection_id| { Some(H160::zero()) } + /// ); + /// ``` + #[macro_export] + macro_rules! impl_precompile_mock { + ($name:ident, $owner_of_collection:expr) => { + struct Erc721Mock; + + impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { + fn owner_of(asset_id: U256) -> Option { + ($owner_of_collection)(asset_id) + } + } + + type $name = Erc721Precompile; + }; + } + + /// Macro to define a precompile mock for testing. + /// + /// This macro creates mock implementations of the `Erc721` trait, + /// allowing you to test how your code interacts with the precompiled contracts. + /// The mock type is named `Mock`, and the implementation uses the provided expressions. + /// + /// # Arguments + /// + /// * `$create_collection_result`: An expression that evaluates to a `DispatchResult`. + /// * `$owner_of_collection_result`: An expression that evaluates to an `Option`. + /// + /// # Example + /// + /// ``` + /// impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::zero())); + /// ``` + #[macro_export] + macro_rules! impl_precompile_mock_simple { + ($name:ident, $owner_of_collection:expr) => { + impl_precompile_mock!($name, |_asset_id| { $owner_of_collection }); + }; + } + + /// Create a mock handle for testing precompiled contracts. + /// + /// This function takes an input string representing the data to be sent to the precompiled contract + /// and a cost value, returning a `MockHandle` that can be used for testing. + /// + /// # Arguments + /// + /// * `input` - The input data as a hexadecimal string. + /// * `cost` - A cost value as u64. + /// * `value` - The amount of coins transferred as u64. + /// + /// # Example + /// + /// ``` + /// let handle = create_mock_handle("68656c6c6f", 0, 0); + /// ``` + pub fn create_mock_handle(input: &str, cost: u64, value: u64, caller: H160) -> MockHandle { + let i: Vec = hex::decode(input).expect("invalid input"); + + let context: Context = + Context { address: Default::default(), caller, apparent_value: From::from(value) }; + + MockHandle::new(i, Some(cost), context) + } + + /// Create a mock handle for testing precompiled contracts without a specific cost or value. + /// + /// This function takes an input string representing the data to be sent to the precompiled contract + /// and returns a `MockHandle` that can be used for testing. + /// + /// # Arguments + /// + /// * `input` - The input data as a hexadecimal string. + /// + /// # Example + /// + /// ``` + /// let handle = create_mock_handle_from_input("68656c6c6f"); + /// ``` + pub fn create_mock_handle_from_input(input: &str) -> MockHandle { + create_mock_handle(input, 0, 0, H160::zero()) + } + + pub struct MockHandle { + pub input: Vec, + pub gas_limit: Option, + pub context: Context, + pub is_static: bool, + pub gas_used: u64, + pub logs: Vec, + } + + impl MockHandle { + pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { + Self { input, gas_limit, context, is_static: false, gas_used: 0, logs: vec![] } + } + } + + impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + _: H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &Context, + ) -> (ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + Ok(()) + } + + fn record_external_cost( + &mut self, + _: Option, + _: Option, + ) -> Result<(), ExitError> { + Ok(()) + } + + fn refund_external_cost(&mut self, _: Option, _: Option) {} + + fn log( + &mut self, + address: H160, + topics: Vec, + data: Vec, + ) -> Result<(), ExitError> { + let log = Log { address, topics, data }; + self.logs.push(log); + Ok(()) + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn code_address(&self) -> H160 { + unimplemented!() + } + + fn input(&self) -> &[u8] { + &self.input + } + + fn context(&self) -> &Context { + &self.context + } + + fn is_static(&self) -> bool { + self.is_static + } + + fn gas_limit(&self) -> Option { + self.gas_limit + } + } +} diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 8884c218..08171c00 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -9,7 +9,6 @@ use helpers::*; use sp_core::{H160, H256}; use sp_std::vec::Vec; -type CollectionId = u64; type AccountId = H160; type AddressMapping = pallet_evm::IdentityAddressMapping; From cec3cbf77045abf45a472172ca9804f5f20f7cc8 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 15:47:04 +0200 Subject: [PATCH 015/114] first implermentation --- precompile/erc721/src/lib.rs | 7 +++---- precompile/erc721/src/tests.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 01560caa..74a5574d 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -4,8 +4,9 @@ use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::traits::Erc721; use parity_scale_codec::Encode; -use precompile_utils::{EvmResult, FunctionModifier, PrecompileHandleExt}; +use precompile_utils::{succeed, EvmResult, FunctionModifier, PrecompileHandleExt}; +use sp_core::H160; use sp_std::{fmt::Debug, marker::PhantomData}; #[precompile_utils_macro::generate_function_selector] @@ -45,9 +46,7 @@ where Action::TockenURI => { todo!() }, - Action::OwnerOf => { - todo!() - }, + Action::OwnerOf => Ok(succeed(H160::zero().encode())), } } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 640e8c46..1d796e52 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -19,6 +19,7 @@ fn create_collection_should_return_address() { let mut handle = create_mock_handle_from_input(owner_of_1234); let result = Mock::execute(&mut handle); assert!(result.is_ok()); + assert_eq!(result.unwrap().output, H160::zero().encode()); } mod helpers { From b0c10ce2be2a7e0880d3b03153de8c9933149646 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 16:13:56 +0200 Subject: [PATCH 016/114] first integration of erc721 in the runtime --- Cargo.lock | 43 ++++++++++--------- Cargo.toml | 1 + pallets/living-assets-ownership/src/lib.rs | 7 +++ pallets/living-assets-ownership/src/traits.rs | 4 +- precompile/erc721/Cargo.toml | 2 +- runtime/Cargo.toml | 1 + runtime/src/precompiles.rs | 19 +++++++- 7 files changed, 52 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 222f2646..2b2325d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5060,6 +5060,7 @@ dependencies = [ "pallet-ethereum", "pallet-evm", "pallet-evm-chain-id", + "pallet-evm-erc721", "pallet-evm-living-assets-ownership", "pallet-evm-precompile-modexp", "pallet-evm-precompile-simple", @@ -7027,6 +7028,27 @@ dependencies = [ "sp-runtime", ] +[[package]] +name = "pallet-evm-erc721" +version = "0.0.1" +dependencies = [ + "evm", + "fp-evm", + "frame-support", + "hex", + "num_enum 0.5.11", + "pallet-evm", + "pallet-living-assets-ownership", + "parity-scale-codec", + "precompile-utils", + "precompile-utils-macro", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-evm-living-assets-ownership" version = "2.0.0-dev" @@ -9396,27 +9418,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "precompile-erc721" -version = "0.0.1" -dependencies = [ - "evm", - "fp-evm", - "frame-support", - "hex", - "num_enum 0.5.11", - "pallet-evm", - "pallet-living-assets-ownership", - "parity-scale-codec", - "precompile-utils", - "precompile-utils-macro", - "scale-info", - "sp-arithmetic", - "sp-core", - "sp-runtime", - "sp-std", -] - [[package]] name = "precompile-utils" version = "0.4.3" diff --git a/Cargo.toml b/Cargo.toml index d73afa85..ac213229 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -222,6 +222,7 @@ pallet-bridge-relayers = { git = "https://github.com/freeverseio/parity-bridges- # LAOS pallets pallet-living-assets-ownership = { path = "./pallets/living-assets-ownership", default-features = false } pallet-evm-living-assets-ownership = { path = "./precompile/living-assets", default-features = false } +pallet-evm-erc721 = { path = "./precompile/erc721", default-features = false } # Utils precompile-utils = { path = "./precompile/utils", default-features = false } diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 29ef5d26..154a5f4a 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -13,6 +13,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::{OptionQuery, ValueQuery, *}; use frame_system::pallet_prelude::*; + use sp_core::{U256, H160}; /// Collection id type /// TODO: use 256 bits @@ -83,6 +84,12 @@ pub mod pallet { Self::do_create_collection(owner) } } + + impl traits::Erc721 for Pallet { + fn owner_of(_asset_id: U256) -> Option { + Some(H160::zero()) + } + } } #[cfg(test)] diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index a9975100..69f4c46a 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,4 +1,4 @@ -use sp_core::{U256, H160}; +use sp_core::{H160, U256}; use crate::CollectionId; @@ -30,4 +30,4 @@ pub trait CollectionManager { pub trait Erc721 { fn owner_of(asset_id: U256) -> Option; -} \ No newline at end of file +} diff --git a/precompile/erc721/Cargo.toml b/precompile/erc721/Cargo.toml index 58f587d5..47304b70 100644 --- a/precompile/erc721/Cargo.toml +++ b/precompile/erc721/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "precompile-erc721" +name = "pallet-evm-erc721" version = "0.0.1" edition = "2021" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index f0039b08..50784833 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -50,6 +50,7 @@ sp-version = { workspace = true } pallet-living-assets-ownership = { workspace = true } pallet-evm-living-assets-ownership = { workspace = true } +pallet-evm-erc721 = { workspace = true } # Polkadot pallet-xcm = { workspace = true } diff --git a/runtime/src/precompiles.rs b/runtime/src/precompiles.rs index 63e6aaa9..8ce48a30 100644 --- a/runtime/src/precompiles.rs +++ b/runtime/src/precompiles.rs @@ -8,6 +8,7 @@ use sp_core::H160; use sp_std::marker::PhantomData; use pallet_evm_living_assets_ownership::CollectionManagerPrecompile; +use pallet_evm_erc721::Erc721Precompile; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; @@ -33,12 +34,28 @@ type LivingAssetsPrecompile = CollectionManagerPrecompile< pallet_living_assets_ownership::Pallet, >; +type Erc721 = Erc721Precompile< + pallet_evm::HashedAddressMapping, + AccountId, + pallet_living_assets_ownership::Pallet, +>; + impl PrecompileSet for FrontierPrecompiles where Runtime: pallet_evm::Config + pallet_living_assets_ownership::Config, { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - match handle.code_address() { + let code_address = handle.code_address(); + + // TODO put the following code in the right place, maybe a fuction in the precompile + let first_byte = code_address.0[0]; + // Check if the first bit is set to 1 + if first_byte & 0x80 == 0x80 { + Some(Erc721::execute(handle)); + () + } + + match code_address { // Ethereum precompiles : a if a == hash(1) => Some(ECRecover::execute(handle)), a if a == hash(2) => Some(Sha256::execute(handle)), From 1420c2145e2898ff9806e00d166f91b1368c2ff5 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 16:16:09 +0200 Subject: [PATCH 017/114] fmt --- pallets/living-assets-ownership/src/lib.rs | 2 +- runtime/src/precompiles.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 154a5f4a..bc79a627 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -13,7 +13,7 @@ pub mod pallet { use super::*; use frame_support::pallet_prelude::{OptionQuery, ValueQuery, *}; use frame_system::pallet_prelude::*; - use sp_core::{U256, H160}; + use sp_core::{H160, U256}; /// Collection id type /// TODO: use 256 bits diff --git a/runtime/src/precompiles.rs b/runtime/src/precompiles.rs index 8ce48a30..6c2f7b40 100644 --- a/runtime/src/precompiles.rs +++ b/runtime/src/precompiles.rs @@ -7,8 +7,8 @@ use polkadot_primitives::BlakeTwo256; use sp_core::H160; use sp_std::marker::PhantomData; -use pallet_evm_living_assets_ownership::CollectionManagerPrecompile; use pallet_evm_erc721::Erc721Precompile; +use pallet_evm_living_assets_ownership::CollectionManagerPrecompile; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; @@ -38,7 +38,7 @@ type Erc721 = Erc721Precompile< pallet_evm::HashedAddressMapping, AccountId, pallet_living_assets_ownership::Pallet, ->; +>; impl PrecompileSet for FrontierPrecompiles where From d0c4ce71b6d286c059b8a8ba8f78894dbf401114 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 16:54:16 +0200 Subject: [PATCH 018/114] check on the collection id --- pallets/living-assets-ownership/src/lib.rs | 7 +++++-- pallets/living-assets-ownership/src/traits.rs | 2 +- precompile/erc721/src/tests.rs | 7 ++++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index bc79a627..890d0cdb 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -86,8 +86,11 @@ pub mod pallet { } impl traits::Erc721 for Pallet { - fn owner_of(_asset_id: U256) -> Option { - Some(H160::zero()) + fn owner_of(collection_id: CollectionId, _asset_id: U256) -> Option { + match OwnerOfCollection::::get(collection_id) { + Some(_) => Some(H160::zero()), + None => None, + } } } } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 69f4c46a..500cd5c8 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -29,5 +29,5 @@ pub trait CollectionManager { } pub trait Erc721 { - fn owner_of(asset_id: U256) -> Option; + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Option; } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 1d796e52..16c8a9e6 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,5 +1,6 @@ use super::*; use helpers::*; +use pallet_living_assets_ownership::CollectionId; use sp_core::{H160, U256}; type AccountId = H160; @@ -54,8 +55,8 @@ mod helpers { struct Erc721Mock; impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { - fn owner_of(asset_id: U256) -> Option { - ($owner_of_collection)(asset_id) + fn owner_of(collectio_id: CollectionId, asset_id: U256) -> Option { + ($owner_of_collection)(collectio_id, asset_id) } } @@ -82,7 +83,7 @@ mod helpers { #[macro_export] macro_rules! impl_precompile_mock_simple { ($name:ident, $owner_of_collection:expr) => { - impl_precompile_mock!($name, |_asset_id| { $owner_of_collection }); + impl_precompile_mock!($name, |_asset_id, _collection_id| { $owner_of_collection }); }; } From aa84dde48abdb8900d4860c0d21236a345877586 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 4 Aug 2023 17:21:00 +0200 Subject: [PATCH 019/114] test on extract owner form asset_id --- .../living-assets-ownership/src/functions.rs | 18 ++++++++++++++++++ pallets/living-assets-ownership/src/lib.rs | 4 ++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index acf3d69e..88eddf39 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -1,6 +1,7 @@ //! Contains helper and utility functions of the pallet use super::*; use frame_support::sp_runtime::traits::One; +use sp_core::{Encode, H160, U256}; impl Pallet { /// See [Self::create_collection] @@ -23,3 +24,20 @@ impl Pallet { Ok(collection_id) } } + +pub fn convert_u256_to_h160(value: U256) -> H160 { + let bytes = value.encode(); + H160::from_slice(&bytes[12..32]) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_convert_u256_to_h160() { + let value = U256::from(5); + let expected_address = H160::from_low_u64_be(5); + assert_eq!(convert_u256_to_h160(value), expected_address); + } +} diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 890d0cdb..5dc79830 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -86,9 +86,9 @@ pub mod pallet { } impl traits::Erc721 for Pallet { - fn owner_of(collection_id: CollectionId, _asset_id: U256) -> Option { + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Option { match OwnerOfCollection::::get(collection_id) { - Some(_) => Some(H160::zero()), + Some(_) => Some(functions::convert_u256_to_h160(asset_id)), None => None, } } From 0ec72a77f59cae598aaacd8ac8c72cc3ba98dbaa Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Sat, 5 Aug 2023 07:25:10 +0200 Subject: [PATCH 020/114] fix tests --- pallets/living-assets-ownership/src/functions.rs | 14 ++++++++------ pallets/living-assets-ownership/src/lib.rs | 4 +++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index 88eddf39..abf2b028 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -1,7 +1,7 @@ //! Contains helper and utility functions of the pallet use super::*; use frame_support::sp_runtime::traits::One; -use sp_core::{Encode, H160, U256}; +use sp_core::{H160, U256}; impl Pallet { /// See [Self::create_collection] @@ -25,9 +25,11 @@ impl Pallet { } } -pub fn convert_u256_to_h160(value: U256) -> H160 { - let bytes = value.encode(); - H160::from_slice(&bytes[12..32]) +pub fn convert_asset_id_to_owner(value: U256) -> H160 { + let mut bytes = [0u8; 20]; + let value_bytes: [u8; 32] = value.into(); + bytes.copy_from_slice(&value_bytes[value_bytes.len() - 20..]); + H160::from(bytes) } #[cfg(test)] @@ -35,9 +37,9 @@ mod tests { use super::*; #[test] - fn check_convert_u256_to_h160() { + fn check_convert_asset_id_to_owner() { let value = U256::from(5); let expected_address = H160::from_low_u64_be(5); - assert_eq!(convert_u256_to_h160(value), expected_address); + assert_eq!(convert_asset_id_to_owner(value), expected_address); } } diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 5dc79830..1ac933a7 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -10,6 +10,8 @@ pub mod traits; #[frame_support::pallet] pub mod pallet { + use crate::functions::convert_asset_id_to_owner; + use super::*; use frame_support::pallet_prelude::{OptionQuery, ValueQuery, *}; use frame_system::pallet_prelude::*; @@ -88,7 +90,7 @@ pub mod pallet { impl traits::Erc721 for Pallet { fn owner_of(collection_id: CollectionId, asset_id: U256) -> Option { match OwnerOfCollection::::get(collection_id) { - Some(_) => Some(functions::convert_u256_to_h160(asset_id)), + Some(_) => Some(convert_asset_id_to_owner(asset_id)), None => None, } } From 9be1a5b2f4b0ba267706ed3fa292edb29ea80cee Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Sat, 5 Aug 2023 08:27:15 +0200 Subject: [PATCH 021/114] test for erc721 trait --- pallets/living-assets-ownership/src/tests.rs | 26 +++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index dd37b935..7d663c12 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,5 +1,10 @@ -use crate::{mock::*, traits::CollectionManager, Event}; +use crate::{ + mock::*, + traits::{CollectionManager, Erc721}, + Event, +}; use frame_support::assert_ok; +use sp_core::H160; type AccountId = ::AccountId; @@ -107,3 +112,22 @@ fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { ); }); } + +#[test] +fn erc721_owner_of_asset_of_unexistent_collection() { + new_test_ext().execute_with(|| { + assert_eq!(::owner_of(0, 2.into()), None); + }); +} + +#[test] +fn erc721_owner_of_asset_of_collection() { + new_test_ext().execute_with(|| { + let collection_id = + >::create_collection(ALICE).unwrap(); + assert_eq!( + ::owner_of(collection_id, 2.into()).unwrap(), + H160::from_low_u64_be(0x0000000000000002) + ); + }); +} From 4625aad06967473ca4d68638f5268b72b7484506 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Sat, 5 Aug 2023 09:44:45 +0200 Subject: [PATCH 022/114] fix compilation --- Cargo.lock | 365 +++++++++++++++----------------- runtime/src/precompiles/mock.rs | 16 +- 2 files changed, 180 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b2325d8..369d6c12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -316,7 +316,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.23", ] [[package]] @@ -332,7 +332,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.23", ] [[package]] @@ -424,25 +424,25 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "asynchronous-codec" -version = "0.6.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +checksum = "06a0daa378f5fd10634e44b0a29b2a87b890657658e072a30d6f26e57ddee182" dependencies = [ "bytes", "futures-sink", @@ -576,13 +576,13 @@ dependencies = [ "lazy_static", "lazycell", "peeking_take_while", - "prettyplease 0.2.12", + "prettyplease 0.2.10", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -733,7 +733,7 @@ dependencies = [ [[package]] name = "bp-beefy" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "binary-merkle-tree", "bp-runtime", @@ -751,7 +751,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-cumulus" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -766,7 +766,7 @@ dependencies = [ [[package]] name = "bp-evochain" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-beefy", "bp-header-chain", @@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-runtime", "finality-grandpa", @@ -810,7 +810,7 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-runtime", @@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "bp-ownership-parachain" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -843,7 +843,7 @@ dependencies = [ [[package]] name = "bp-parachains" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -860,7 +860,7 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-messages", "bp-runtime", @@ -878,7 +878,7 @@ dependencies = [ [[package]] name = "bp-relayers" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-messages", "bp-runtime", @@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "frame-support", "frame-system", @@ -914,7 +914,7 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-parachains", @@ -934,7 +934,7 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-messages", @@ -1081,12 +1081,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.81" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c6b2562119bf28c3439f7f02db99faf0aa1a8cdfe5772a2ee155d32227239f0" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", - "libc", ] [[package]] @@ -1111,9 +1110,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.15.4" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b40ccee03b5175c18cde8f37e7d2a33bcef6f8ec8f7cc0d81090d1bb380949c9" +checksum = "215c0072ecc28f92eeb0eea38ba63ddfcb65c2828c46311d646f1a3ff5f9841c" dependencies = [ "smallvec", ] @@ -1233,9 +1232,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.19" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "5b0827b011f6f8ab38590295339817b0d26f344aa4932c3ced71b45b0c54b4a9" dependencies = [ "clap_builder", "clap_derive", @@ -1244,9 +1243,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "9441b403be87be858db6a23edb493e7f694761acdc3343d5a0fcaafd304cbc9e" dependencies = [ "anstream", "anstyle", @@ -1263,7 +1262,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -1989,7 +1988,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -2293,9 +2292,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f68e12e817cb19eaab81aaec582b4052d07debd3c3c6b083b9d361db47c7dc9d" +checksum = "5032837c1384de3708043de9d4e97bb91290faca6c16529a28aa340592a78166" dependencies = [ "cc", "cxxbridge-flags", @@ -2305,9 +2304,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e789217e4ab7cf8cc9ce82253180a9fe331f35f5d339f0ccfe0270b39433f397" +checksum = "51368b3d0dbf356e10fcbfd455a038503a105ee556f7ee79b6bb8c53a7247456" dependencies = [ "cc", "codespan-reporting", @@ -2315,24 +2314,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "cxxbridge-flags" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78a19f4c80fd9ab6c882286fa865e92e07688f4387370a209508014ead8751d0" +checksum = "0d9062157072e4aafc8e56ceaf8325ce850c5ae37578c852a0d4de2cecdded13" [[package]] name = "cxxbridge-macro" -version = "1.0.102" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fcfa71f66c8563c4fa9dd2bb68368d50267856f831ac5d85367e0805f9606c" +checksum = "cf01e8a540f5a4e0f284595834f81cf88572f244b768f051724537afa99a2545" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -2445,12 +2444,6 @@ dependencies = [ "rusticata-macros", ] -[[package]] -name = "deranged" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929" - [[package]] name = "derivative" version = "2.2.0" @@ -2602,7 +2595,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -2626,7 +2619,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.28", + "syn 2.0.26", "termcolor", "walkdir", ] @@ -2684,9 +2677,9 @@ dependencies = [ [[package]] name = "ecdsa" -version = "0.16.8" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ "der 0.7.7", "digest 0.10.7", @@ -2735,9 +2728,9 @@ dependencies = [ [[package]] name = "either" -version = "1.9.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" @@ -2815,18 +2808,18 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "enumn" -version = "0.1.11" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b893c4eb2dc092c811165f84dc7447fae16fb66521717968c34c509b39b1a5c5" +checksum = "c9838a970f5de399d3070ae1739e131986b2f5dcc223c7423ca0927e3a878522" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -2869,9 +2862,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", @@ -3040,7 +3033,7 @@ dependencies = [ "fs-err", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -3064,12 +3057,6 @@ dependencies = [ "instant", ] -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - [[package]] name = "fatality" version = "0.0.6" @@ -3580,7 +3567,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -3699,7 +3686,7 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -3711,7 +3698,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -3721,7 +3708,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -3801,7 +3788,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" dependencies = [ - "rustix 0.38.6", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -3866,7 +3853,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand 1.9.0", + "fastrand", "futures-core", "futures-io", "memchr", @@ -3883,7 +3870,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -4033,9 +4020,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "globset" -version = "0.4.12" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ "aho-corasick", "bstr 1.6.0", @@ -4267,9 +4254,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" @@ -4485,9 +4472,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.6" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b297dc40733f23a0e52728a58fa9489a5b7638a324932de16b41adc3ef80730" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" dependencies = [ "console", "instant", @@ -4590,7 +4577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.2", - "rustix 0.38.6", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -4778,7 +4765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ "cfg-if", - "ecdsa 0.16.8", + "ecdsa 0.16.7", "elliptic-curve 0.13.5", "once_cell", "sha2 0.10.7", @@ -5620,9 +5607,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.12" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "pkg-config", @@ -5676,9 +5663,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" @@ -5770,7 +5757,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -5784,7 +5771,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -5795,7 +5782,7 @@ checksum = "c12469fc165526520dff2807c2975310ab47cf7190a45b99b49a7dc8befab17b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -5806,7 +5793,7 @@ checksum = "b8fb85ec1620619edf2984a7693497d4ec88a9665d8b87e942856884c92dbf2a" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -6328,9 +6315,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] @@ -6384,7 +6371,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -6743,7 +6730,7 @@ dependencies = [ [[package]] name = "pallet-bridge-grandpa" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-runtime", @@ -6764,7 +6751,7 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-messages", @@ -6785,7 +6772,7 @@ dependencies = [ [[package]] name = "pallet-bridge-parachains" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-header-chain", "bp-parachains", @@ -6806,7 +6793,7 @@ dependencies = [ [[package]] name = "pallet-bridge-relayers" version = "0.1.0" -source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#79808a3e640f882c49162f5f24c9ea48e7b35a0a" +source = "git+https://github.com/freeverseio/parity-bridges-common.git?branch=polkadot-v1.0.0#60a56bbad7ad646e71a9924a1cfeda61c47c4b2f" dependencies = [ "bp-messages", "bp-relayers", @@ -7562,7 +7549,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -7844,9 +7831,9 @@ dependencies = [ [[package]] name = "parity-db" -version = "0.4.10" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f19d20a0d2cc52327a88d131fa1c4ea81ea4a04714aedcfeca2dd410049cf8" +checksum = "0dab3ac198341b2f0fec6e7f8a6eeed07a41201d98a124260611598c142e76df" dependencies = [ "blake2", "crc32fast", @@ -8046,9 +8033,9 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" dependencies = [ "thiserror", "ucd-trie", @@ -8056,9 +8043,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" dependencies = [ "pest", "pest_generator", @@ -8066,22 +8053,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "pest_meta" -version = "2.7.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" dependencies = [ "once_cell", "pest", @@ -8115,7 +8102,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -9408,9 +9395,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.4.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" +checksum = "edc55135a600d700580e406b4de0d59cb9ad25e344a3a091a97ded2622ec4ec6" [[package]] name = "ppv-lite86" @@ -9495,12 +9482,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.12" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +checksum = "92139198957b410250d43fad93e630d956499a625c527eda65175c8680f83387" dependencies = [ "proc-macro2", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -9581,7 +9568,7 @@ checksum = "70550716265d1ec349c41f70dd4f964b4fd88394efe4405f0c1da679c4799a07" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -9734,9 +9721,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.9.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31999cfc7927c4e212e60fd50934ab40e8e8bfd2d493d6095d2d306bc0764d9" +checksum = "67c10f662eee9c94ddd7135043e544f3c82fa839a1e7b865911331961b53186c" dependencies = [ "bytes", "rand 0.8.5", @@ -9752,9 +9739,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" dependencies = [ "proc-macro2", ] @@ -9881,7 +9868,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.25", + "time 0.3.23", "x509-parser 0.13.2", "yasna", ] @@ -9894,7 +9881,7 @@ checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ "pem", "ring", - "time 0.3.25", + "time 0.3.23", "yasna", ] @@ -9942,22 +9929,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.19" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" +checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.19" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" +checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -9980,7 +9967,7 @@ checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.4", + "regex-automata 0.3.3", "regex-syntax 0.7.4", ] @@ -9995,9 +9982,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.4" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -10331,14 +10318,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.6" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ "bitflags 2.3.3", "errno", "libc", - "linux-raw-sys 0.4.5", + "linux-raw-sys 0.4.3", "windows-sys 0.48.0", ] @@ -10555,7 +10542,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -11478,7 +11465,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -11703,9 +11690,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -11716,9 +11703,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -11750,29 +11737,29 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.181" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890" +checksum = "e91f70896d6720bc714a4a57d22fc91f1db634680e65c8efe13323f1fa38d53f" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.181" +version = "1.0.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be02f6cb0cd3a5ec20bbcfbcbd749f57daddb1a0882dc2e46a6c236c90b977ed" +checksum = "a6250dde8342e0232232be9ca3db7aa40aceb5a3e5dd9bddbc00d99a007cde49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "d03b412469450d4404fe8499a268edd7f8b79fecb074b0d812ad64ca21f4031b" dependencies = [ "itoa", "ryu", @@ -12075,7 +12062,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -12312,7 +12299,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "quote", "sp-core-hashing", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -12331,7 +12318,7 @@ source = "git+https://github.com/paritytech/substrate?branch=polkadot-v1.0.0#948 dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -12538,7 +12525,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -12723,7 +12710,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -13043,9 +13030,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.28" +version = "2.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" +checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" dependencies = [ "proc-macro2", "quote", @@ -13093,20 +13080,21 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.11" +version = "0.12.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0e916b1148c8e263850e1ebcbd046f333e0683c724876bb0da63ea4373dc8a" +checksum = "1d2faeef5759ab89935255b1a4cd98e0baf99d1085e37d36599c625dac49ae8e" [[package]] name = "tempfile" -version = "3.7.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", - "fastrand 2.0.0", + "fastrand", "redox_syscall 0.3.5", - "rustix 0.38.6", + "rustix 0.37.23", "windows-sys 0.48.0", ] @@ -13127,22 +13115,22 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.44" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -13185,9 +13173,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-ctl" -version = "0.5.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "619bfed27d807b54f7f776b9430d4f8060e66ee138a28632ca898584d462c31c" +checksum = "e37706572f4b151dff7a0146e040804e9c26fe3a3118591112f05cf12a4216c1" dependencies = [ "libc", "paste", @@ -13196,9 +13184,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.5.4+5.3.0-patched" +version = "0.5.3+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9402443cb8fd499b6f327e40565234ff34dbda27460c5b47db0db77443dd85d1" +checksum = "a678df20055b43e57ef8cddde41cdfda9a3c1a060b67f4c5836dfb1d78543ba8" dependencies = [ "cc", "libc", @@ -13217,11 +13205,10 @@ dependencies = [ [[package]] name = "time" -version = "0.3.25" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ - "deranged", "itoa", "serde", "time-core", @@ -13236,9 +13223,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.11" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -13324,7 +13311,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -13442,9 +13429,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.3" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" +checksum = "7ac8060a61f8758a61562f6fb53ba3cbe1ca906f001df2e53cccddcdbee91e7c" dependencies = [ "bitflags 2.3.3", "bytes", @@ -13491,7 +13478,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -13534,7 +13521,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -13962,7 +13949,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", "wasm-bindgen-shared", ] @@ -13996,7 +13983,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -14341,7 +14328,7 @@ dependencies = [ "sha2 0.10.7", "stun", "thiserror", - "time 0.3.25", + "time 0.3.23", "tokio", "turn", "url", @@ -14875,9 +14862,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.5.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46aab759304e4d7b2075a9aecba26228bb073ee8c50db796b2c72c676b5d807" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] @@ -14939,7 +14926,7 @@ dependencies = [ "ring", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.23", ] [[package]] @@ -14957,7 +14944,7 @@ dependencies = [ "oid-registry 0.6.1", "rusticata-macros", "thiserror", - "time 0.3.25", + "time 0.3.23", ] [[package]] @@ -15026,7 +15013,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] @@ -15049,7 +15036,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.25", + "time 0.3.23", ] [[package]] @@ -15069,7 +15056,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.28", + "syn 2.0.26", ] [[package]] diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs index d8b58b46..bf680f02 100644 --- a/runtime/src/precompiles/mock.rs +++ b/runtime/src/precompiles/mock.rs @@ -9,7 +9,6 @@ use pallet_evm::{ }; use sp_core::{H160, H256, U256}; use sp_runtime::{ - testing::Header, traits::{BlakeTwo256, IdentityLookup}, ConsensusEngineId, }; @@ -17,16 +16,11 @@ use sp_std::{boxed::Box, prelude::*, str::FromStr}; use super::FrontierPrecompiles; -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; // Configure a mock runtime to test the pallet. construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { + pub enum Runtime { System: frame_system, Balances: pallet_balances, Timestamp: pallet_timestamp, @@ -42,13 +36,10 @@ impl frame_system::Config for Runtime { type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = H160; type Lookup = IdentityLookup; - type Header = Header; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; type Version = (); @@ -60,6 +51,8 @@ impl frame_system::Config for Runtime { type SS58Prefix = (); type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; + type Nonce = u64; + type Block = Block; } parameter_types! { @@ -74,12 +67,12 @@ impl pallet_balances::Config for Runtime { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type ReserveIdentifier = (); - type HoldIdentifier = (); type FreezeIdentifier = (); type MaxLocks = (); type MaxReserves = (); type MaxHolds = (); type MaxFreezes = (); + type RuntimeHoldReason = (); } parameter_types! { @@ -95,7 +88,6 @@ impl pallet_timestamp::Config for Runtime { impl pallet_living_assets_ownership::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type CollectionId = u64; } pub struct FixedGasPrice; From d858bf01881a1c5c6417b190232f4a8c7b8c484e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Sat, 5 Aug 2023 15:03:58 +0200 Subject: [PATCH 023/114] refactoring --- runtime/src/precompiles.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/runtime/src/precompiles.rs b/runtime/src/precompiles.rs index 6c2f7b40..14acb723 100644 --- a/runtime/src/precompiles.rs +++ b/runtime/src/precompiles.rs @@ -40,22 +40,20 @@ type Erc721 = Erc721Precompile< pallet_living_assets_ownership::Pallet, >; +impl FrontierPrecompiles { + fn is_erc721_address(&self, address: H160) -> bool { + let first_byte = address.0[0]; + // Check if the first bit is set to 1 + first_byte & 0x80 == 0x80 + } +} + impl PrecompileSet for FrontierPrecompiles where Runtime: pallet_evm::Config + pallet_living_assets_ownership::Config, { fn execute(&self, handle: &mut impl PrecompileHandle) -> Option { - let code_address = handle.code_address(); - - // TODO put the following code in the right place, maybe a fuction in the precompile - let first_byte = code_address.0[0]; - // Check if the first bit is set to 1 - if first_byte & 0x80 == 0x80 { - Some(Erc721::execute(handle)); - () - } - - match code_address { + match handle.code_address() { // Ethereum precompiles : a if a == hash(1) => Some(ECRecover::execute(handle)), a if a == hash(2) => Some(Sha256::execute(handle)), @@ -66,11 +64,16 @@ where // a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), a if a == hash(1025) => Some(ECRecoverPublicKey::execute(handle)), a if a == hash(1026) => Some(LivingAssetsPrecompile::execute(handle)), + a if self.is_erc721_address(a) => Some(Erc721::execute(handle)), _ => None, } } fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { + if self.is_erc721_address(address) { + return IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 }; + } + IsPrecompileResult::Answer { is_precompile: Self::used_addresses().contains(&address), extra_cost: 0, From 6c65ddde881ba138a8bbf7b59eb66533b52db361 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Sun, 6 Aug 2023 06:19:02 +0200 Subject: [PATCH 024/114] returning address --- precompile/erc721/src/lib.rs | 10 ++++++++-- precompile/erc721/src/tests.rs | 20 +++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 74a5574d..74fc1aa9 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -4,7 +4,9 @@ use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::traits::Erc721; use parity_scale_codec::Encode; -use precompile_utils::{succeed, EvmResult, FunctionModifier, PrecompileHandleExt}; +use precompile_utils::{ + succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, +}; use sp_core::H160; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -46,7 +48,11 @@ where Action::TockenURI => { todo!() }, - Action::OwnerOf => Ok(succeed(H160::zero().encode())), + Action::OwnerOf => { + let byte20 = H160::zero(); + let address = Address::from(byte20); + Ok(succeed(EvmDataWriter::new().write(address).build())) + }, } } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 16c8a9e6..06e78cd2 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -13,16 +13,30 @@ fn check_selectors() { } #[test] -fn create_collection_should_return_address() { +fn owner_of_unexistent_should_return_null_address() { impl_precompile_mock_simple!(Mock, None); - let owner_of_1234 = "6352211e00000000000000000000000000000000000000000000000000000000000004d2"; + let owner_of_1234 = "6352211e0000000000000000000000000000000000000000000000000000000000000004"; let mut handle = create_mock_handle_from_input(owner_of_1234); let result = Mock::execute(&mut handle); assert!(result.is_ok()); - assert_eq!(result.unwrap().output, H160::zero().encode()); + assert_eq!(result.unwrap().output, vec![0u8; 32]); } +// #[test] +// fn owner_of() { +// impl_precompile_mock!(Mock, |collection_id, asset_id| { +// assert_eq!(collection_id, 0); +// assert_eq!(asset_id, U256::from("0x1234")); +// Some(H160::from(2)) +// }); + +// let owner_of_1234 = "6352211e0000000000000000000000000000000000000000000000000000000000001234"; +// let mut handle = create_mock_handle_from_input(owner_of_1234); +// let result = Mock::execute(&mut handle); +// assert!(result.is_ok()); +// assert_eq!(result.unwrap().output, H160::zero().encode()); +// } mod helpers { use evm::{Context, ExitError, ExitReason, Transfer}; use fp_evm::{Log, PrecompileHandle}; From 1c9e12a64770b9a589810f02e75480ba9d498bd3 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 11:13:46 +0200 Subject: [PATCH 025/114] return value is correctly encoded as a n Address --- precompile/living-assets/src/lib.rs | 5 +++-- precompile/living-assets/src/tests.rs | 15 ++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index 1ee99544..ec9e1ffc 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -5,7 +5,8 @@ use fp_evm::{ExitError, Precompile, PrecompileFailure, PrecompileHandle, Precomp use pallet_living_assets_ownership::{traits::CollectionManager, CollectionId}; use parity_scale_codec::Encode; use precompile_utils::{ - keccak256, succeed, EvmResult, FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt, + keccak256, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, LogExt, LogsBuilder, + PrecompileHandleExt, }; use sp_runtime::SaturatedConversion; @@ -60,7 +61,7 @@ where .log2(SELECTOR_LOG_CREATE_COLLECTION, collection_address, Vec::new()) .record(handle)?; - Ok(succeed(collection_address.encode())) + Ok(succeed(EvmDataWriter::new().write(Address(collection_address)).build())) }, Err(err) => Err(PrecompileFailure::Error { exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed(err)), diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 73ca99f4..ff2e058b 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -6,7 +6,7 @@ use super::*; use evm::ExitRevert; use helpers::*; -use sp_core::{H160, H256}; +use sp_core::H160; use sp_std::vec::Vec; type CollectionId = u64; @@ -60,14 +60,14 @@ fn create_collection_should_return_address() { assert!(result.is_ok()); // check that the output is the collection id 0 assert_eq!( - result.unwrap().output, - hex::decode("8000000000000000000000000000000000000005").unwrap() + hex::encode(result.unwrap().output), + "0000000000000000000000008000000000000000000000000000000000000005" ); } #[test] fn create_collection_should_generate_log() { - impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); + impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(H160::zero())); let mut handle = create_mock_handle_from_input(CREATE_COLLECTION); let result = Mock::execute(&mut handle); @@ -78,11 +78,8 @@ fn create_collection_should_generate_log() { assert_eq!(logs[0].topics.len(), 2); assert_eq!(logs[0].topics[0], SELECTOR_LOG_CREATE_COLLECTION.into()); assert_eq!( - logs[0].topics[1], - H256::from_slice( - &hex::decode("0000000000000000000000008000000000000000000000000000000000000005") - .unwrap() - ) + hex::encode(logs[0].topics[1]), + "000000000000000000000000800000000000000000000000000000000000ffff" ); assert_eq!(logs[0].data, Vec::::new()); } From 9dc0c14d28bf329dad6015f0889151a7cf6590a1 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 12:08:26 +0200 Subject: [PATCH 026/114] is_erc721_contract is not member of PrecompoileSet --- runtime/src/precompiles/mod.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 9f43ab66..3266b603 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -40,14 +40,6 @@ type Erc721 = Erc721Precompile< pallet_living_assets_ownership::Pallet, >; -impl FrontierPrecompiles { - fn is_erc721_address(&self, address: H160) -> bool { - let first_byte = address.0[0]; - // Check if the first bit is set to 1 - first_byte & 0x80 == 0x80 - } -} - impl PrecompileSet for FrontierPrecompiles where Runtime: pallet_evm::Config + pallet_living_assets_ownership::Config, @@ -64,13 +56,13 @@ where // a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), a if a == hash(1025) => Some(ECRecoverPublicKey::execute(handle)), a if a == hash(1026) => Some(LivingAssetsPrecompile::execute(handle)), - a if self.is_erc721_address(a) => Some(Erc721::execute(handle)), + a if is_erc721_address(a) => Some(Erc721::execute(handle)), _ => None, } } fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { - if self.is_erc721_address(address) { + if is_erc721_address(address) { return IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 }; } @@ -85,6 +77,12 @@ fn hash(a: u64) -> H160 { H160::from_low_u64_be(a) } +fn is_erc721_address(address: H160) -> bool { + let first_byte = address.0[0]; + // Check if the first bit is set to 1 + first_byte & 0x80 == 0x80 +} + #[cfg(test)] mod mock; From b9bba2dfde9f3f6e193e153f761374529afab738 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 12:46:14 +0200 Subject: [PATCH 027/114] test on precompiled contracts --- runtime/src/precompiles/test.rs | 34 ++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index f9c475da..83698b01 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -1,4 +1,5 @@ -use super::{hash, mock::*, FrontierPrecompiles}; +use super::{hash, is_erc721_address, mock::*, FrontierPrecompiles}; +use core::str::FromStr; use pallet_evm::{IsPrecompileResult, PrecompileSet}; use sp_core::H160; @@ -22,4 +23,35 @@ fn ethereum_precompiled_addresses_are_precompile() { assert!(is_precompile(hash(3)).unwrap()); assert!(is_precompile(hash(4)).unwrap()); assert!(is_precompile(hash(5)).unwrap()); + assert!(is_precompile(hash(1026)).unwrap()); + assert!(is_precompile(H160::from_str("0x8000000000000000000000000000000000000001").unwrap()) + .unwrap()); +} + +#[test] +fn check_for_erc721_addresses() { + assert!(!is_erc721_address( + H160::from_str("0x1000000000000000000000000000000000000001").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0x8000000000000000000000000000000000000000").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0x8000000000000000000000000000000000000001").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0x8000000000000000000000000000000000000002").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0x8000000000000000000000000000000000000003").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0x80000000000000000000000000000000ffffffff").unwrap() + )); + assert!(!is_erc721_address( + H160::from_str("0x7fffffffffffffffffffffffffffffffffffffff").unwrap() + )); + assert!(is_erc721_address( + H160::from_str("0xffffffffffffffffffffffffffffffffffffffff").unwrap() + )); } From a39432cc875890da4e151e9445b560ee26e7eddb Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 12:51:29 +0200 Subject: [PATCH 028/114] erc721 returns hardcoded address --- precompile/erc721/src/lib.rs | 7 ++++--- precompile/erc721/src/tests.rs | 5 ++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 74fc1aa9..65168177 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,6 +1,8 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] +use core::str::FromStr; + use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::traits::Erc721; use parity_scale_codec::Encode; @@ -49,9 +51,8 @@ where todo!() }, Action::OwnerOf => { - let byte20 = H160::zero(); - let address = Address::from(byte20); - Ok(succeed(EvmDataWriter::new().write(address).build())) + let address = H160::from_str("0x0000000000000000000000000000000012345678").unwrap(); + Ok(succeed(EvmDataWriter::new().write(Address(address)).build())) }, } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 06e78cd2..f519e102 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -20,7 +20,10 @@ fn owner_of_unexistent_should_return_null_address() { let mut handle = create_mock_handle_from_input(owner_of_1234); let result = Mock::execute(&mut handle); assert!(result.is_ok()); - assert_eq!(result.unwrap().output, vec![0u8; 32]); + assert_eq!( + hex::encode(result.unwrap().output), + "0000000000000000000000000000000000000000000000000000000012345678" + ); } // #[test] From c4cd42f1ac747c9aba56491750f394df5b338b38 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 15:40:28 +0200 Subject: [PATCH 029/114] refactoring --- Cargo.lock | 2 ++ pallets/living-assets-ownership/Cargo.toml | 2 ++ pallets/living-assets-ownership/src/lib.rs | 13 +++++++++++++ pallets/living-assets-ownership/src/tests.rs | 17 +++++++++++++++++ precompile/living-assets/src/lib.rs | 16 +++------------- precompile/living-assets/src/tests.rs | 9 --------- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 369d6c12..c99b7a51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7178,6 +7178,8 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hex", + "hex-literal 0.4.1", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index 47227a05..ce931860 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -12,6 +12,7 @@ edition = "2021" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +hex-literal = { workspace = true, optional = true} parity-scale-codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } @@ -24,6 +25,7 @@ sp-core = { workspace = true } [dev-dependencies] serde = { workspace = true } +hex = { version = "0.4.3" } # Substrate sp-core = { workspace = true } diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 1ac933a7..a7ebdce0 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -4,6 +4,7 @@ /// Learn more about FRAME and the core library of Substrate FRAME pallets: /// pub use pallet::*; +use sp_core::H160; mod functions; pub mod traits; @@ -97,6 +98,18 @@ pub mod pallet { } } +pub fn collection_id_to_address(collection_id: CollectionId) -> H160 { + let mut address = H160::from_low_u64_be(collection_id); + address.0[0] |= 0x80; // Set the first bit to 1 + address +} + +pub fn address_to_collection_id(address: H160) -> CollectionId { + let bytes: [u8; 20] = address.into(); + let id_bytes: [u8; 8] = bytes[12..20].try_into().expect("Slice length doesn't match"); + CollectionId::from_be_bytes(id_bytes) +} + #[cfg(test)] mod mock; diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 7d663c12..603fe1d6 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,4 +1,7 @@ +use core::str::FromStr; + use crate::{ + address_to_collection_id, collection_id_to_address, mock::*, traits::{CollectionManager, Erc721}, Event, @@ -131,3 +134,17 @@ fn erc721_owner_of_asset_of_collection() { ); }); } + +#[test] +fn test_collection_id_to_address() { + let collection_id: u64 = 5; + let expected_address = H160::from_str("8000000000000000000000000000000000000005").unwrap(); + assert_eq!(collection_id_to_address(collection_id), expected_address); +} + +#[test] +fn test_address_to_collection_id() { + let address = H160::from_str("8000000000000000000000000000000000000005").unwrap(); + let collection_it = address_to_collection_id(address); + assert_eq!(collection_it, 5); +} diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index ec9e1ffc..da770c8c 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -2,7 +2,9 @@ #![cfg_attr(not(feature = "std"), no_std)] use fp_evm::{ExitError, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput}; -use pallet_living_assets_ownership::{traits::CollectionManager, CollectionId}; +use pallet_living_assets_ownership::{ + collection_id_to_address, traits::CollectionManager, CollectionId, +}; use parity_scale_codec::Encode; use precompile_utils::{ keccak256, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, LogExt, LogsBuilder, @@ -72,17 +74,5 @@ where } } -/// Converts a `u64` collection ID to an `H160` address. -/// -/// This function takes a `u64` collection ID and converts it into an `H160` address by using the -/// `from_low_u64_be` method to convert the `u64` value into the lower 64 bits of the `H160`. -/// Additionally, the function sets the first bit of the resulting `H160` to 1, which can be used to -/// distinguish addresses created by this function from other addresses. -fn collection_id_to_address(collection_id: CollectionId) -> H160 { - let mut address = H160::from_low_u64_be(collection_id); - address.0[0] |= 0x80; // Set the first bit to 1 - address -} - #[cfg(test)] mod tests; diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 16f83ac1..6c52a568 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -27,15 +27,6 @@ fn check_log_selectors() { ); } -#[test] -fn test_collection_id_to_address() { - let collection_id: u64 = 5; - let hex_value = "8000000000000000000000000000000000000005"; - let bytes = hex::decode(hex_value).expect("Decoding failed"); - let expected_address = H160::from_slice(&bytes); - assert_eq!(collection_id_to_address(collection_id), expected_address); -} - #[test] fn failing_create_collection_should_return_error() { impl_precompile_mock_simple!(Mock, Err("spaghetti code"), Some(H160::zero())); From 89af0a0088a8584b94dcbb993365b05e96faa38d Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 16:03:17 +0200 Subject: [PATCH 030/114] testing return of asset ownership --- precompile/erc721/src/lib.rs | 17 +++++++++++------ precompile/erc721/src/tests.rs | 29 ++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 65168177..669d6f51 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,16 +1,14 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -use core::str::FromStr; - use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; -use pallet_living_assets_ownership::traits::Erc721; +use pallet_living_assets_ownership::{address_to_collection_id, traits::Erc721}; use parity_scale_codec::Encode; use precompile_utils::{ succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, }; -use sp_core::H160; +use sp_core::U256; use sp_std::{fmt::Debug, marker::PhantomData}; #[precompile_utils_macro::generate_function_selector] @@ -51,8 +49,15 @@ where todo!() }, Action::OwnerOf => { - let address = H160::from_str("0x0000000000000000000000000000000012345678").unwrap(); - Ok(succeed(EvmDataWriter::new().write(Address(address)).build())) + let mut input = handle.read_input()?; + input.expect_arguments(1)?; + + let asset_id: U256 = input.read()?; + + // collection id is encoded into the contract address + let collection_id = address_to_collection_id(handle.code_address()); + let owner = AssetManager::owner_of(collection_id, asset_id).unwrap(); + Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())) }, } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index f519e102..5d385e7a 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,3 +1,5 @@ +use core::str::FromStr; + use super::*; use helpers::*; use pallet_living_assets_ownership::CollectionId; @@ -13,16 +15,20 @@ fn check_selectors() { } #[test] -fn owner_of_unexistent_should_return_null_address() { - impl_precompile_mock_simple!(Mock, None); +fn owner_of_asset_should_return_an_address() { + impl_precompile_mock_simple!( + Mock, + Some(H160::from_str("ff00000000000000000000000000000012345678").unwrap()) + ); - let owner_of_1234 = "6352211e0000000000000000000000000000000000000000000000000000000000000004"; - let mut handle = create_mock_handle_from_input(owner_of_1234); + let owner_of_asset_4 = + "6352211e0000000000000000000000000000000000000000000000000000000000000004"; + let mut handle = create_mock_handle_from_input(owner_of_asset_4); let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( hex::encode(result.unwrap().output), - "0000000000000000000000000000000000000000000000000000000012345678" + "000000000000000000000000ff00000000000000000000000000000012345678", ); } @@ -154,11 +160,20 @@ mod helpers { pub is_static: bool, pub gas_used: u64, pub logs: Vec, + pub code_address: H160, } impl MockHandle { pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { - Self { input, gas_limit, context, is_static: false, gas_used: 0, logs: vec![] } + Self { + input, + gas_limit, + context, + is_static: false, + gas_used: 0, + logs: vec![], + code_address: H160::zero(), + } } } @@ -208,7 +223,7 @@ mod helpers { } fn code_address(&self) -> H160 { - unimplemented!() + self.code_address } fn input(&self) -> &[u8] { From c6c78102ac29e4ef4315895c0cbf9c396bcf8750 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 16:18:55 +0200 Subject: [PATCH 031/114] fix errors --- pallets/living-assets-ownership/src/lib.rs | 6 ++--- pallets/living-assets-ownership/src/tests.rs | 5 +++- pallets/living-assets-ownership/src/traits.rs | 2 +- precompile/erc721/src/lib.rs | 10 ++++--- precompile/erc721/src/tests.rs | 26 +++++++++++++++++-- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index a7ebdce0..9cfe5ced 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -89,10 +89,10 @@ pub mod pallet { } impl traits::Erc721 for Pallet { - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Option { + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { match OwnerOfCollection::::get(collection_id) { - Some(_) => Some(convert_asset_id_to_owner(asset_id)), - None => None, + Some(_) => Ok(convert_asset_id_to_owner(asset_id)), + None => Err("Collection does not exist"), } } } diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 603fe1d6..91f1bb91 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -119,7 +119,10 @@ fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { #[test] fn erc721_owner_of_asset_of_unexistent_collection() { new_test_ext().execute_with(|| { - assert_eq!(::owner_of(0, 2.into()), None); + assert_eq!( + ::owner_of(0, 2.into()), + Err("Collection does not exist") + ); }); } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 500cd5c8..2e1e8d86 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -29,5 +29,5 @@ pub trait CollectionManager { } pub trait Erc721 { - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Option; + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; } diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 669d6f51..928fb02e 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,7 +1,7 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; +use fp_evm::{ExitError, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{address_to_collection_id, traits::Erc721}; use parity_scale_codec::Encode; use precompile_utils::{ @@ -56,8 +56,12 @@ where // collection id is encoded into the contract address let collection_id = address_to_collection_id(handle.code_address()); - let owner = AssetManager::owner_of(collection_id, asset_id).unwrap(); - Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())) + match AssetManager::owner_of(collection_id, asset_id) { + Ok(owner) => Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())), + Err(err) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed(err)), + }), + } }, } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 5d385e7a..a9744889 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,6 +1,8 @@ use core::str::FromStr; use super::*; +use evm::ExitError; +use fp_evm::PrecompileFailure; use helpers::*; use pallet_living_assets_ownership::CollectionId; use sp_core::{H160, U256}; @@ -18,7 +20,7 @@ fn check_selectors() { fn owner_of_asset_should_return_an_address() { impl_precompile_mock_simple!( Mock, - Some(H160::from_str("ff00000000000000000000000000000012345678").unwrap()) + Ok(H160::from_str("ff00000000000000000000000000000012345678").unwrap()) ); let owner_of_asset_4 = @@ -32,6 +34,23 @@ fn owner_of_asset_should_return_an_address() { ); } +#[test] +fn owner_of_if_fails_returns_error() { + impl_precompile_mock_simple!(Mock, Err("spaghetti error")); + + let owner_of_asset_4 = + "6352211e0000000000000000000000000000000000000000000000000000000000000004"; + let mut handle = create_mock_handle_from_input(owner_of_asset_4); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + PrecompileFailure::Error { + exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed("spaghetti error")) + } + ); +} + // #[test] // fn owner_of() { // impl_precompile_mock!(Mock, |collection_id, asset_id| { @@ -78,7 +97,10 @@ mod helpers { struct Erc721Mock; impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { - fn owner_of(collectio_id: CollectionId, asset_id: U256) -> Option { + fn owner_of( + collectio_id: CollectionId, + asset_id: U256, + ) -> Result { ($owner_of_collection)(collectio_id, asset_id) } } From 5cfb2b54fd85543850780c8ba6e7c9d37a99fc82 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 16:21:27 +0200 Subject: [PATCH 032/114] remove unused use --- precompile/living-assets/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index da770c8c..a7956049 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -12,7 +12,6 @@ use precompile_utils::{ }; use sp_runtime::SaturatedConversion; -use sp_core::H160; use sp_std::{fmt::Debug, marker::PhantomData, vec::Vec}; /// Solidity selector of the CreateCollection log, which is the Keccak of the Log signature. From 874d2f638b51eeaff2f10992e99c28017ff2ffca Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 16:52:38 +0200 Subject: [PATCH 033/114] create collection got an uri --- precompile/living-assets/contracts/LivingAssetsOwnership.sol | 2 +- precompile/living-assets/src/lib.rs | 2 +- precompile/living-assets/src/tests.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/precompile/living-assets/contracts/LivingAssetsOwnership.sol b/precompile/living-assets/contracts/LivingAssetsOwnership.sol index 04c7e087..5b87e687 100644 --- a/precompile/living-assets/contracts/LivingAssetsOwnership.sol +++ b/precompile/living-assets/contracts/LivingAssetsOwnership.sol @@ -13,5 +13,5 @@ interface LivingAssets { /// @notice Creates a new collection /// @dev Call this function to create a new collection /// @return address of the ERC721 collection - function createCollection() external returns (address); + function createCollection(string memory tokenURI) external returns (address); } diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index a7956049..099ac047 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -21,7 +21,7 @@ pub const SELECTOR_LOG_CREATE_COLLECTION: [u8; 32] = keccak256!("CreateCollectio #[derive(Debug, PartialEq)] pub enum Action { /// Create collection - CreateCollection = "createCollection()", + CreateCollection = "createCollection(string)", } /// Wrapper for the precompile function. diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 6c52a568..c4efba63 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -12,11 +12,11 @@ use sp_std::vec::Vec; type AccountId = H160; type AddressMapping = pallet_evm::IdentityAddressMapping; -const CREATE_COLLECTION: &str = "647f1a9c"; +const CREATE_COLLECTION: &str = "0x059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; #[test] fn check_selectors() { - assert_eq!(Action::CreateCollection as u32, 0x647F1A9C); + assert_eq!(Action::CreateCollection as u32, 0x059dfe13); } #[test] From 0568c47c1554fc5715a05c2cfc06cb9e373373de Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 20:54:19 +0200 Subject: [PATCH 034/114] refactoring --- precompile/erc721/Cargo.toml | 1 + precompile/erc721/src/tests.rs | 96 +-------------------------- precompile/living-assets/Cargo.toml | 1 + precompile/living-assets/src/tests.rs | 87 +----------------------- precompile/utils/src/lib.rs | 2 + precompile/utils/src/mock.rs | 83 +++++++++++++++++++++++ precompile/utils/src/testing.rs | 12 ++++ 7 files changed, 105 insertions(+), 177 deletions(-) create mode 100644 precompile/utils/src/mock.rs diff --git a/precompile/erc721/Cargo.toml b/precompile/erc721/Cargo.toml index 47304b70..04d19196 100644 --- a/precompile/erc721/Cargo.toml +++ b/precompile/erc721/Cargo.toml @@ -34,6 +34,7 @@ num_enum = { workspace = true } [dev-dependencies] evm = { workspace = true } hex = { version = "0.4.3" } +precompile-utils = { workspace = true, features = ["testing"]} [features] default = ["std"] diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index a9744889..b28a3201 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -66,9 +66,9 @@ fn owner_of_if_fails_returns_error() { // assert_eq!(result.unwrap().output, H160::zero().encode()); // } mod helpers { - use evm::{Context, ExitError, ExitReason, Transfer}; - use fp_evm::{Log, PrecompileHandle}; - use sp_core::{H160, H256}; + use evm::Context; + use precompile_utils::mock::MockHandle; + use sp_core::H160; /// Macro to define a precompile mock with custom closures for testing. /// @@ -174,94 +174,4 @@ mod helpers { pub fn create_mock_handle_from_input(input: &str) -> MockHandle { create_mock_handle(input, 0, 0, H160::zero()) } - - pub struct MockHandle { - pub input: Vec, - pub gas_limit: Option, - pub context: Context, - pub is_static: bool, - pub gas_used: u64, - pub logs: Vec, - pub code_address: H160, - } - - impl MockHandle { - pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { - Self { - input, - gas_limit, - context, - is_static: false, - gas_used: 0, - logs: vec![], - code_address: H160::zero(), - } - } - } - - impl PrecompileHandle for MockHandle { - /// Perform subcall in provided context. - /// Precompile specifies in which context the subcall is executed. - fn call( - &mut self, - _: H160, - _: Option, - _: Vec, - _: Option, - _: bool, - _: &Context, - ) -> (ExitReason, Vec) { - unimplemented!() - } - - fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { - self.gas_used += cost; - Ok(()) - } - - fn record_external_cost( - &mut self, - _: Option, - _: Option, - ) -> Result<(), ExitError> { - Ok(()) - } - - fn refund_external_cost(&mut self, _: Option, _: Option) {} - - fn log( - &mut self, - address: H160, - topics: Vec, - data: Vec, - ) -> Result<(), ExitError> { - let log = Log { address, topics, data }; - self.logs.push(log); - Ok(()) - } - - fn remaining_gas(&self) -> u64 { - unimplemented!() - } - - fn code_address(&self) -> H160 { - self.code_address - } - - fn input(&self) -> &[u8] { - &self.input - } - - fn context(&self) -> &Context { - &self.context - } - - fn is_static(&self) -> bool { - self.is_static - } - - fn gas_limit(&self) -> Option { - self.gas_limit - } - } } diff --git a/precompile/living-assets/Cargo.toml b/precompile/living-assets/Cargo.toml index 9edd2bdc..57d0c67c 100644 --- a/precompile/living-assets/Cargo.toml +++ b/precompile/living-assets/Cargo.toml @@ -38,6 +38,7 @@ num_enum = { workspace = true } [dev-dependencies] evm = { workspace = true } hex = { version = "0.4.3" } +precompile-utils = { workspace = true, features = ["testing"]} [features] default = ["std"] diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 5a212fb9..e372311b 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -123,9 +123,9 @@ fn call_unexistent_selector_should_fail() { } mod helpers { - use evm::{Context, ExitError, ExitReason, Transfer}; - use fp_evm::{Log, PrecompileHandle}; - use sp_core::{H160, H256}; + use evm::Context; + use precompile_utils::mock::MockHandle; + use sp_core::H160; /// Macro to define a precompile mock for testing. /// @@ -235,85 +235,4 @@ mod helpers { pub fn create_mock_handle_from_input(input: &str) -> MockHandle { create_mock_handle(input, 0, 0, H160::zero()) } - - pub struct MockHandle { - pub input: Vec, - pub gas_limit: Option, - pub context: Context, - pub is_static: bool, - pub gas_used: u64, - pub logs: Vec, - } - - impl MockHandle { - pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { - Self { input, gas_limit, context, is_static: false, gas_used: 0, logs: vec![] } - } - } - - impl PrecompileHandle for MockHandle { - /// Perform subcall in provided context. - /// Precompile specifies in which context the subcall is executed. - fn call( - &mut self, - _: H160, - _: Option, - _: Vec, - _: Option, - _: bool, - _: &Context, - ) -> (ExitReason, Vec) { - unimplemented!() - } - - fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { - self.gas_used += cost; - Ok(()) - } - - fn record_external_cost( - &mut self, - _: Option, - _: Option, - ) -> Result<(), ExitError> { - Ok(()) - } - - fn refund_external_cost(&mut self, _: Option, _: Option) {} - - fn log( - &mut self, - address: H160, - topics: Vec, - data: Vec, - ) -> Result<(), ExitError> { - let log = Log { address, topics, data }; - self.logs.push(log); - Ok(()) - } - - fn remaining_gas(&self) -> u64 { - unimplemented!() - } - - fn code_address(&self) -> H160 { - unimplemented!() - } - - fn input(&self) -> &[u8] { - &self.input - } - - fn context(&self) -> &Context { - &self.context - } - - fn is_static(&self) -> bool { - self.is_static - } - - fn gas_limit(&self) -> Option { - self.gas_limit - } - } } diff --git a/precompile/utils/src/lib.rs b/precompile/utils/src/lib.rs index 9f23028e..15880bcc 100644 --- a/precompile/utils/src/lib.rs +++ b/precompile/utils/src/lib.rs @@ -40,6 +40,8 @@ mod data; pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; pub use precompile_utils_macro::{generate_function_selector, keccak256}; +#[cfg(feature = "testing")] +pub mod mock; #[cfg(feature = "testing")] pub mod testing; #[cfg(test)] diff --git a/precompile/utils/src/mock.rs b/precompile/utils/src/mock.rs new file mode 100644 index 00000000..d1ef955d --- /dev/null +++ b/precompile/utils/src/mock.rs @@ -0,0 +1,83 @@ +use super::*; +use fp_evm::{ExitReason, Transfer}; + +pub struct MockHandle { + pub input: Vec, + pub gas_limit: Option, + pub context: Context, + pub is_static: bool, + pub gas_used: u64, + pub logs: Vec, + pub code_address: H160, +} + +impl MockHandle { + pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { + Self { + input, + gas_limit, + context, + is_static: false, + gas_used: 0, + logs: vec![], + code_address: H160::zero(), + } + } +} + +impl PrecompileHandle for MockHandle { + /// Perform subcall in provided context. + /// Precompile specifies in which context the subcall is executed. + fn call( + &mut self, + _: H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &Context, + ) -> (ExitReason, Vec) { + unimplemented!() + } + + fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { + self.gas_used += cost; + Ok(()) + } + + fn record_external_cost(&mut self, _: Option, _: Option) -> Result<(), ExitError> { + Ok(()) + } + + fn refund_external_cost(&mut self, _: Option, _: Option) {} + + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { + let log = Log { address, topics, data }; + self.logs.push(log); + Ok(()) + } + + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + + fn code_address(&self) -> H160 { + self.code_address + } + + fn input(&self) -> &[u8] { + &self.input + } + + fn context(&self) -> &Context { + &self.context + } + + fn is_static(&self) -> bool { + self.is_static + } + + fn gas_limit(&self) -> Option { + self.gas_limit + } +} diff --git a/precompile/utils/src/testing.rs b/precompile/utils/src/testing.rs index 8733bbd7..c96ec567 100644 --- a/precompile/utils/src/testing.rs +++ b/precompile/utils/src/testing.rs @@ -224,6 +224,18 @@ impl PrecompileHandle for MockHandle { fn gas_limit(&self) -> Option { Some(self.gas_limit) } + + fn record_external_cost( + &mut self, + _ref_time: Option, + _proof_size: Option, + ) -> Result<(), ExitError> { + todo!() + } + + fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) { + todo!() + } } pub struct PrecompilesTester<'p, P> { From b31f1db259e19cc9f2c435494a3a82a0d09fbcc2 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 20:58:24 +0200 Subject: [PATCH 035/114] rewriting testing mod --- precompile/erc721/src/tests.rs | 2 +- precompile/living-assets/src/tests.rs | 2 +- precompile/utils/src/lib.rs | 2 - precompile/utils/src/mock.rs | 83 ------ precompile/utils/src/testing.rs | 399 ++------------------------ 5 files changed, 32 insertions(+), 456 deletions(-) delete mode 100644 precompile/utils/src/mock.rs diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index b28a3201..10e425d2 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -67,7 +67,7 @@ fn owner_of_if_fails_returns_error() { // } mod helpers { use evm::Context; - use precompile_utils::mock::MockHandle; + use precompile_utils::testing::MockHandle; use sp_core::H160; /// Macro to define a precompile mock with custom closures for testing. diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index e372311b..f7c77907 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -124,7 +124,7 @@ fn call_unexistent_selector_should_fail() { mod helpers { use evm::Context; - use precompile_utils::mock::MockHandle; + use precompile_utils::testing::MockHandle; use sp_core::H160; /// Macro to define a precompile mock for testing. diff --git a/precompile/utils/src/lib.rs b/precompile/utils/src/lib.rs index 15880bcc..9f23028e 100644 --- a/precompile/utils/src/lib.rs +++ b/precompile/utils/src/lib.rs @@ -40,8 +40,6 @@ mod data; pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; pub use precompile_utils_macro::{generate_function_selector, keccak256}; -#[cfg(feature = "testing")] -pub mod mock; #[cfg(feature = "testing")] pub mod testing; #[cfg(test)] diff --git a/precompile/utils/src/mock.rs b/precompile/utils/src/mock.rs deleted file mode 100644 index d1ef955d..00000000 --- a/precompile/utils/src/mock.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::*; -use fp_evm::{ExitReason, Transfer}; - -pub struct MockHandle { - pub input: Vec, - pub gas_limit: Option, - pub context: Context, - pub is_static: bool, - pub gas_used: u64, - pub logs: Vec, - pub code_address: H160, -} - -impl MockHandle { - pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { - Self { - input, - gas_limit, - context, - is_static: false, - gas_used: 0, - logs: vec![], - code_address: H160::zero(), - } - } -} - -impl PrecompileHandle for MockHandle { - /// Perform subcall in provided context. - /// Precompile specifies in which context the subcall is executed. - fn call( - &mut self, - _: H160, - _: Option, - _: Vec, - _: Option, - _: bool, - _: &Context, - ) -> (ExitReason, Vec) { - unimplemented!() - } - - fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { - self.gas_used += cost; - Ok(()) - } - - fn record_external_cost(&mut self, _: Option, _: Option) -> Result<(), ExitError> { - Ok(()) - } - - fn refund_external_cost(&mut self, _: Option, _: Option) {} - - fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { - let log = Log { address, topics, data }; - self.logs.push(log); - Ok(()) - } - - fn remaining_gas(&self) -> u64 { - unimplemented!() - } - - fn code_address(&self) -> H160 { - self.code_address - } - - fn input(&self) -> &[u8] { - &self.input - } - - fn context(&self) -> &Context { - &self.context - } - - fn is_static(&self) -> bool { - self.is_static - } - - fn gas_limit(&self) -> Option { - self.gas_limit - } -} diff --git a/precompile/utils/src/testing.rs b/precompile/utils/src/testing.rs index c96ec567..d1ef955d 100644 --- a/precompile/utils/src/testing.rs +++ b/precompile/utils/src/testing.rs @@ -1,422 +1,83 @@ -// This file is part of Astar. - -// Copyright 2019-2022 PureStake Inc. -// Copyright (C) 2022-2023 Stake Technologies Pte.Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later -// -// This file is part of Utils package, originally developed by Purestake Inc. -// Utils package used in Astar Network in terms of GPLv3. -// -// Utils 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. - -// Utils 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 Utils. If not, see . use super::*; -use assert_matches::assert_matches; -use fp_evm::{ - ExitReason, ExitSucceed, PrecompileOutput, PrecompileResult, PrecompileSet, Transfer, -}; -use sp_std::boxed::Box; - -pub struct Subcall { - pub address: H160, - pub transfer: Option, - pub input: Vec, - pub target_gas: Option, - pub is_static: bool, - pub context: Context, -} +use fp_evm::{ExitReason, Transfer}; -pub struct SubcallOutput { - pub reason: ExitReason, - pub output: Vec, - pub cost: u64, - pub logs: Vec, -} - -pub trait SubcallTrait: FnMut(Subcall) -> SubcallOutput + 'static {} - -impl SubcallOutput + 'static> SubcallTrait for T {} - -pub type SubcallHandle = Box; - -/// Mock handle to write tests for precompiles. pub struct MockHandle { - pub gas_limit: u64, - pub gas_used: u64, - pub logs: Vec, - pub subcall_handle: Option, - pub code_address: H160, pub input: Vec, + pub gas_limit: Option, pub context: Context, pub is_static: bool, + pub gas_used: u64, + pub logs: Vec, + pub code_address: H160, } impl MockHandle { - pub fn new(code_address: H160, context: Context) -> Self { + pub fn new(input: Vec, gas_limit: Option, context: Context) -> Self { Self { - gas_limit: u64::MAX, - gas_used: 0, - logs: vec![], - subcall_handle: None, - code_address, - input: Vec::new(), + input, + gas_limit, context, is_static: false, + gas_used: 0, + logs: vec![], + code_address: H160::zero(), } } } -// Compute the cost of doing a subcall. -// Some parameters cannot be known in advance, so we estimate the worst possible cost. -pub fn call_cost(value: U256, config: &evm::Config) -> u64 { - // Copied from EVM code since not public. - pub const G_CALLVALUE: u64 = 9000; - pub const G_NEWACCOUNT: u64 = 25000; - - fn address_access_cost(is_cold: bool, regular_value: u64, config: &evm::Config) -> u64 { - if config.increase_state_access_gas { - if is_cold { - config.gas_account_access_cold - } else { - config.gas_storage_read_warm - } - } else { - regular_value - } - } - - fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { - if is_call_or_callcode && transfers_value { - G_CALLVALUE - } else { - 0 - } - } - - fn new_cost( - is_call_or_staticcall: bool, - new_account: bool, - transfers_value: bool, - config: &evm::Config, - ) -> u64 { - let eip161 = !config.empty_considered_exists; - if is_call_or_staticcall { - if eip161 { - if transfers_value && new_account { - G_NEWACCOUNT - } else { - 0 - } - } else if new_account { - G_NEWACCOUNT - } else { - 0 - } - } else { - 0 - } - } - - let transfers_value = value != U256::default(); - let is_cold = true; - let is_call_or_callcode = true; - let is_call_or_staticcall = true; - let new_account = true; - - address_access_cost(is_cold, config.gas_call, config) - + xfer_cost(is_call_or_callcode, transfers_value) - + new_cost(is_call_or_staticcall, new_account, transfers_value, config) -} - impl PrecompileHandle for MockHandle { /// Perform subcall in provided context. /// Precompile specifies in which context the subcall is executed. fn call( &mut self, - address: H160, - transfer: Option, - input: Vec, - target_gas: Option, - is_static: bool, - context: &Context, + _: H160, + _: Option, + _: Vec, + _: Option, + _: bool, + _: &Context, ) -> (ExitReason, Vec) { - if self - .record_cost(call_cost(context.apparent_value, &evm::Config::london())) - .is_err() - { - return (ExitReason::Error(ExitError::OutOfGas), vec![]); - } - - match &mut self.subcall_handle { - Some(handle) => { - let SubcallOutput { reason, output, cost, logs } = handle(Subcall { - address, - transfer, - input, - target_gas, - is_static, - context: context.clone(), - }); - - if self.record_cost(cost).is_err() { - return (ExitReason::Error(ExitError::OutOfGas), vec![]); - } - - for log in logs { - self.log(log.address, log.topics, log.data).expect("cannot fail"); - } - - (reason, output) - }, - None => panic!("no subcall handle registered"), - } + unimplemented!() } fn record_cost(&mut self, cost: u64) -> Result<(), ExitError> { self.gas_used += cost; - - if self.gas_used > self.gas_limit { - Err(ExitError::OutOfGas) - } else { - Ok(()) - } + Ok(()) } - fn remaining_gas(&self) -> u64 { - self.gas_limit - self.gas_used + fn record_external_cost(&mut self, _: Option, _: Option) -> Result<(), ExitError> { + Ok(()) } + fn refund_external_cost(&mut self, _: Option, _: Option) {} + fn log(&mut self, address: H160, topics: Vec, data: Vec) -> Result<(), ExitError> { - self.logs.push(PrettyLog(Log { address, topics, data })); + let log = Log { address, topics, data }; + self.logs.push(log); Ok(()) } - /// Retreive the code address (what is the address of the precompile being called). + fn remaining_gas(&self) -> u64 { + unimplemented!() + } + fn code_address(&self) -> H160 { self.code_address } - /// Retreive the input data the precompile is called with. fn input(&self) -> &[u8] { &self.input } - /// Retreive the context in which the precompile is executed. fn context(&self) -> &Context { &self.context } - /// Is the precompile call is done statically. fn is_static(&self) -> bool { self.is_static } - /// Retreive the gas limit of this call. fn gas_limit(&self) -> Option { - Some(self.gas_limit) - } - - fn record_external_cost( - &mut self, - _ref_time: Option, - _proof_size: Option, - ) -> Result<(), ExitError> { - todo!() - } - - fn refund_external_cost(&mut self, _ref_time: Option, _proof_size: Option) { - todo!() - } -} - -pub struct PrecompilesTester<'p, P> { - precompiles: &'p P, - handle: MockHandle, - - target_gas: Option, - subcall_handle: Option, - - expected_cost: Option, - expected_logs: Option>, -} - -impl<'p, P: PrecompileSet> PrecompilesTester<'p, P> { - pub fn new( - precompiles: &'p P, - from: impl Into, - to: impl Into, - data: Vec, - ) -> Self { - let to = to.into(); - let mut handle = MockHandle::new( - to, - Context { address: to, caller: from.into(), apparent_value: U256::zero() }, - ); - - handle.input = data; - - Self { - precompiles, - handle, - - target_gas: None, - subcall_handle: None, - - expected_cost: None, - expected_logs: None, - } - } - - pub fn with_value(mut self, value: impl Into) -> Self { - self.handle.context.apparent_value = value.into(); - self - } - - pub fn with_subcall_handle(mut self, subcall_handle: impl SubcallTrait) -> Self { - self.subcall_handle = Some(Box::new(subcall_handle)); - self - } - - pub fn with_target_gas(mut self, target_gas: Option) -> Self { - self.target_gas = target_gas; - self - } - - pub fn expect_cost(mut self, cost: u64) -> Self { - self.expected_cost = Some(cost); - self - } - - pub fn expect_no_logs(mut self) -> Self { - self.expected_logs = Some(vec![]); - self - } - - pub fn expect_log(mut self, log: Log) -> Self { - self.expected_logs = Some({ - let mut logs = self.expected_logs.unwrap_or_default(); - logs.push(PrettyLog(log)); - logs - }); - self - } - - fn assert_optionals(&self) { - if let Some(cost) = &self.expected_cost { - assert_eq!(&self.handle.gas_used, cost); - } - - if let Some(logs) = &self.expected_logs { - similar_asserts::assert_eq!(&self.handle.logs, logs); - } - } - - fn execute(&mut self) -> Option { - let handle = &mut self.handle; - handle.subcall_handle = self.subcall_handle.take(); - - if let Some(gas_limit) = self.target_gas { - handle.gas_limit = gas_limit; - } - - let res = self.precompiles.execute(handle); - - self.subcall_handle = handle.subcall_handle.take(); - - res - } - - /// Execute the precompile set and expect some precompile to have been executed, regardless of the - /// result. - pub fn execute_some(mut self) { - let res = self.execute(); - assert!(res.is_some()); - self.assert_optionals(); - } - - /// Execute the precompile set and expect no precompile to have been executed. - pub fn execute_none(mut self) { - let res = self.execute(); - assert!(res.is_none()); - self.assert_optionals(); - } - - /// Execute the precompile set and check it returns provided output. - pub fn execute_returns(mut self, output: Vec) { - let res = self.execute(); - assert_eq!(res, Some(Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output }))); - self.assert_optionals(); - } - - /// Execute the precompile set and check if it reverts. - /// Take a closure allowing to perform custom matching on the output. - pub fn execute_reverts(mut self, check: impl Fn(&[u8]) -> bool) { - let res = self.execute(); - assert_matches!( - res, - Some(Err(PrecompileFailure::Revert { output, ..})) - if check(&output) - ); - self.assert_optionals(); - } - - /// Execute the precompile set and check it returns provided output. - pub fn execute_error(mut self, error: ExitError) { - let res = self.execute(); - assert_eq!(res, Some(Err(PrecompileFailure::Error { exit_status: error }))); - self.assert_optionals(); - } -} - -pub trait PrecompileTesterExt: PrecompileSet + Sized { - fn prepare_test( - &self, - from: impl Into, - to: impl Into, - data: Vec, - ) -> PrecompilesTester; -} - -impl PrecompileTesterExt for T { - fn prepare_test( - &self, - from: impl Into, - to: impl Into, - data: Vec, - ) -> PrecompilesTester { - PrecompilesTester::new(self, from, to, data) - } -} - -#[derive(Clone, PartialEq, Eq)] -pub struct PrettyLog(Log); - -impl core::fmt::Debug for PrettyLog { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { - let bytes = self - .0 - .data - .iter() - .map(|b| format!("{:02X}", b)) - .collect::>() - .join(""); - - let message = String::from_utf8(self.0.data.clone()).ok(); - - f.debug_struct("Log") - .field("address", &self.0.address) - .field("topics", &self.0.topics) - .field("data", &bytes) - .field("data_utf8", &message) - .finish() + self.gas_limit } } From 9a4f1006ef6f4f531459a5aa1233affaa8f794c5 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Mon, 7 Aug 2023 21:09:15 +0200 Subject: [PATCH 036/114] refactoring --- precompile/erc721/src/tests.rs | 55 ++------------------- precompile/living-assets/src/tests.rs | 69 ++++++--------------------- precompile/utils/src/testing.rs | 41 ++++++++++++++++ 3 files changed, 61 insertions(+), 104 deletions(-) diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 10e425d2..79d07ab9 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -3,8 +3,8 @@ use core::str::FromStr; use super::*; use evm::ExitError; use fp_evm::PrecompileFailure; -use helpers::*; use pallet_living_assets_ownership::CollectionId; +use precompile_utils::testing::create_mock_handle_from_input; use sp_core::{H160, U256}; type AccountId = H160; @@ -24,7 +24,8 @@ fn owner_of_asset_should_return_an_address() { ); let owner_of_asset_4 = - "6352211e0000000000000000000000000000000000000000000000000000000000000004"; + hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") + .unwrap(); let mut handle = create_mock_handle_from_input(owner_of_asset_4); let result = Mock::execute(&mut handle); assert!(result.is_ok()); @@ -39,7 +40,8 @@ fn owner_of_if_fails_returns_error() { impl_precompile_mock_simple!(Mock, Err("spaghetti error")); let owner_of_asset_4 = - "6352211e0000000000000000000000000000000000000000000000000000000000000004"; + hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") + .unwrap(); let mut handle = create_mock_handle_from_input(owner_of_asset_4); let result = Mock::execute(&mut handle); assert!(result.is_err()); @@ -66,10 +68,6 @@ fn owner_of_if_fails_returns_error() { // assert_eq!(result.unwrap().output, H160::zero().encode()); // } mod helpers { - use evm::Context; - use precompile_utils::testing::MockHandle; - use sp_core::H160; - /// Macro to define a precompile mock with custom closures for testing. /// /// This macro creates mock implementations of the `Erc721` trait, @@ -131,47 +129,4 @@ mod helpers { impl_precompile_mock!($name, |_asset_id, _collection_id| { $owner_of_collection }); }; } - - /// Create a mock handle for testing precompiled contracts. - /// - /// This function takes an input string representing the data to be sent to the precompiled contract - /// and a cost value, returning a `MockHandle` that can be used for testing. - /// - /// # Arguments - /// - /// * `input` - The input data as a hexadecimal string. - /// * `cost` - A cost value as u64. - /// * `value` - The amount of coins transferred as u64. - /// - /// # Example - /// - /// ``` - /// let handle = create_mock_handle("68656c6c6f", 0, 0); - /// ``` - pub fn create_mock_handle(input: &str, cost: u64, value: u64, caller: H160) -> MockHandle { - let i: Vec = hex::decode(input).expect("invalid input"); - - let context: Context = - Context { address: Default::default(), caller, apparent_value: From::from(value) }; - - MockHandle::new(i, Some(cost), context) - } - - /// Create a mock handle for testing precompiled contracts without a specific cost or value. - /// - /// This function takes an input string representing the data to be sent to the precompiled contract - /// and returns a `MockHandle` that can be used for testing. - /// - /// # Arguments - /// - /// * `input` - The input data as a hexadecimal string. - /// - /// # Example - /// - /// ``` - /// let handle = create_mock_handle_from_input("68656c6c6f"); - /// ``` - pub fn create_mock_handle_from_input(input: &str) -> MockHandle { - create_mock_handle(input, 0, 0, H160::zero()) - } } diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index f7c77907..cd16203a 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -5,7 +5,7 @@ use super::*; use evm::ExitRevert; -use helpers::*; +use precompile_utils::testing::{create_mock_handle, create_mock_handle_from_input}; use sp_core::H160; use sp_std::vec::Vec; @@ -31,7 +31,7 @@ fn check_log_selectors() { fn failing_create_collection_should_return_error() { impl_precompile_mock_simple!(Mock, Err("spaghetti code"), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(CREATE_COLLECTION); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); let result = Mock::execute(&mut handle); assert_eq!( result.unwrap_err(), @@ -45,7 +45,7 @@ fn failing_create_collection_should_return_error() { fn create_collection_should_return_address() { impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(CREATE_COLLECTION); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( @@ -58,7 +58,7 @@ fn create_collection_should_return_address() { fn create_collection_should_generate_log() { impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(CREATE_COLLECTION); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); let logs = handle.logs; @@ -76,7 +76,8 @@ fn create_collection_should_generate_log() { #[test] fn create_collection_on_mock_with_nonzero_value_fails() { impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); - let mut handle = create_mock_handle(CREATE_COLLECTION, 0, 1, H160::zero()); + let mut handle = + create_mock_handle(hex::decode(CREATE_COLLECTION).unwrap(), 0, 1, H160::zero()); let result = Mock::execute(&mut handle); assert!(result.is_err()); assert_eq!( @@ -99,7 +100,12 @@ fn create_collection_assign_collection_to_caller() { |_| { Some(H160::zero()) } // Closure for owner_of_collection result ); - let mut handle = create_mock_handle(CREATE_COLLECTION, 0, 0, H160::from_low_u64_be(0x1234)); + let mut handle = create_mock_handle( + hex::decode(CREATE_COLLECTION).unwrap(), + 0, + 0, + H160::from_low_u64_be(0x1234), + ); let result = Mock::execute(&mut handle); assert!(result.is_ok()); } @@ -109,7 +115,9 @@ fn call_unexistent_selector_should_fail() { impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::from_low_u64_be(0x1234))); // unexistent selector - let input = "fb24ae530000000000000000000000000000000000000000000000000000000000000000"; + let input = + hex::decode("fb24ae530000000000000000000000000000000000000000000000000000000000000000") + .unwrap(); let mut handle = create_mock_handle_from_input(input); let result = Mock::execute(&mut handle); assert_eq!( @@ -123,10 +131,6 @@ fn call_unexistent_selector_should_fail() { } mod helpers { - use evm::Context; - use precompile_utils::testing::MockHandle; - use sp_core::H160; - /// Macro to define a precompile mock for testing. /// /// This macro creates mock implementations of the `CollectionManager` trait, @@ -192,47 +196,4 @@ mod helpers { ); }; } - - /// Create a mock handle for testing precompiled contracts. - /// - /// This function takes an input string representing the data to be sent to the precompiled contract - /// and a cost value, returning a `MockHandle` that can be used for testing. - /// - /// # Arguments - /// - /// * `input` - The input data as a hexadecimal string. - /// * `cost` - A cost value as u64. - /// * `value` - The amount of coins transferred as u64. - /// - /// # Example - /// - /// ``` - /// let handle = create_mock_handle("68656c6c6f", 0, 0); - /// ``` - pub fn create_mock_handle(input: &str, cost: u64, value: u64, caller: H160) -> MockHandle { - let i: Vec = hex::decode(input).expect("invalid input"); - - let context: Context = - Context { address: Default::default(), caller, apparent_value: From::from(value) }; - - MockHandle::new(i, Some(cost), context) - } - - /// Create a mock handle for testing precompiled contracts without a specific cost or value. - /// - /// This function takes an input string representing the data to be sent to the precompiled contract - /// and returns a `MockHandle` that can be used for testing. - /// - /// # Arguments - /// - /// * `input` - The input data as a hexadecimal string. - /// - /// # Example - /// - /// ``` - /// let handle = create_mock_handle_from_input("68656c6c6f"); - /// ``` - pub fn create_mock_handle_from_input(input: &str) -> MockHandle { - create_mock_handle(input, 0, 0, H160::zero()) - } } diff --git a/precompile/utils/src/testing.rs b/precompile/utils/src/testing.rs index d1ef955d..1905d300 100644 --- a/precompile/utils/src/testing.rs +++ b/precompile/utils/src/testing.rs @@ -81,3 +81,44 @@ impl PrecompileHandle for MockHandle { self.gas_limit } } + +/// Create a mock handle for testing precompiled contracts. +/// +/// This function takes an input string representing the data to be sent to the precompiled contract +/// and a cost value, returning a `MockHandle` that can be used for testing. +/// +/// # Arguments +/// +/// * `input` - The input data as a hexadecimal string. +/// * `cost` - A cost value as u64. +/// * `value` - The amount of coins transferred as u64. +/// +/// # Example +/// +/// ``` +/// let handle = create_mock_handle("68656c6c6f", 0, 0); +/// ``` +pub fn create_mock_handle(input: Vec, cost: u64, value: u64, caller: H160) -> MockHandle { + let context: Context = + Context { address: Default::default(), caller, apparent_value: From::from(value) }; + + MockHandle::new(input, Some(cost), context) +} + +/// Create a mock handle for testing precompiled contracts without a specific cost or value. +/// +/// This function takes an input string representing the data to be sent to the precompiled contract +/// and returns a `MockHandle` that can be used for testing. +/// +/// # Arguments +/// +/// * `input` - The input data as a hexadecimal string. +/// +/// # Example +/// +/// ``` +/// let handle = create_mock_handle_from_input("68656c6c6f"); +/// ``` +pub fn create_mock_handle_from_input(input: Vec) -> MockHandle { + create_mock_handle(input, 0, 0, H160::zero()) +} From 09738af98e1e8e732f6ecb95bcd5e52d50a113fe Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 10:26:07 +0200 Subject: [PATCH 037/114] moving check for cllection address in pallet --- pallets/living-assets-ownership/src/lib.rs | 6 ++++ pallets/living-assets-ownership/src/tests.rs | 30 +++++++++++++++++++- precompile/utils/src/testing.rs | 12 -------- runtime/src/precompiles/mod.rs | 6 ++-- runtime/src/precompiles/test.rs | 30 +------------------- 5 files changed, 39 insertions(+), 45 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 9cfe5ced..d1d30fe3 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -110,6 +110,12 @@ pub fn address_to_collection_id(address: H160) -> CollectionId { CollectionId::from_be_bytes(id_bytes) } +pub fn is_collection_address(address: H160) -> bool { + let first_byte = address.0[0]; + // Check if the first bit is set to 1 + first_byte & 0x80 == 0x80 +} + #[cfg(test)] mod mock; diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 91f1bb91..4fc0d329 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, traits::{CollectionManager, Erc721}, Event, @@ -151,3 +151,31 @@ fn test_address_to_collection_id() { let collection_it = address_to_collection_id(address); assert_eq!(collection_it, 5); } + +#[test] +fn check_for_erc721_addresses() { + assert!(!is_collection_address( + H160::from_str("0x1000000000000000000000000000000000000001").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0x8000000000000000000000000000000000000000").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0x8000000000000000000000000000000000000001").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0x8000000000000000000000000000000000000002").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0x8000000000000000000000000000000000000003").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0x80000000000000000000000000000000ffffffff").unwrap() + )); + assert!(!is_collection_address( + H160::from_str("0x7fffffffffffffffffffffffffffffffffffffff").unwrap() + )); + assert!(is_collection_address( + H160::from_str("0xffffffffffffffffffffffffffffffffffffffff").unwrap() + )); +} diff --git a/precompile/utils/src/testing.rs b/precompile/utils/src/testing.rs index 1905d300..9ed36bfc 100644 --- a/precompile/utils/src/testing.rs +++ b/precompile/utils/src/testing.rs @@ -92,12 +92,6 @@ impl PrecompileHandle for MockHandle { /// * `input` - The input data as a hexadecimal string. /// * `cost` - A cost value as u64. /// * `value` - The amount of coins transferred as u64. -/// -/// # Example -/// -/// ``` -/// let handle = create_mock_handle("68656c6c6f", 0, 0); -/// ``` pub fn create_mock_handle(input: Vec, cost: u64, value: u64, caller: H160) -> MockHandle { let context: Context = Context { address: Default::default(), caller, apparent_value: From::from(value) }; @@ -113,12 +107,6 @@ pub fn create_mock_handle(input: Vec, cost: u64, value: u64, caller: H160) - /// # Arguments /// /// * `input` - The input data as a hexadecimal string. -/// -/// # Example -/// -/// ``` -/// let handle = create_mock_handle_from_input("68656c6c6f"); -/// ``` pub fn create_mock_handle_from_input(input: Vec) -> MockHandle { create_mock_handle(input, 0, 0, H160::zero()) } diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 3266b603..ad3bc9eb 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -56,13 +56,13 @@ where // a if a == hash(1024) => Some(Sha3FIPS256::execute(handle)), a if a == hash(1025) => Some(ECRecoverPublicKey::execute(handle)), a if a == hash(1026) => Some(LivingAssetsPrecompile::execute(handle)), - a if is_erc721_address(a) => Some(Erc721::execute(handle)), + a if is_collection_address(a) => Some(Erc721::execute(handle)), _ => None, } } fn is_precompile(&self, address: H160, _gas: u64) -> IsPrecompileResult { - if is_erc721_address(address) { + if is_collection_address(address) { return IsPrecompileResult::Answer { is_precompile: true, extra_cost: 0 }; } @@ -77,7 +77,7 @@ fn hash(a: u64) -> H160 { H160::from_low_u64_be(a) } -fn is_erc721_address(address: H160) -> bool { +fn is_collection_address(address: H160) -> bool { let first_byte = address.0[0]; // Check if the first bit is set to 1 first_byte & 0x80 == 0x80 diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index 83698b01..ce6396d1 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -1,4 +1,4 @@ -use super::{hash, is_erc721_address, mock::*, FrontierPrecompiles}; +use super::{hash, mock::*, FrontierPrecompiles}; use core::str::FromStr; use pallet_evm::{IsPrecompileResult, PrecompileSet}; use sp_core::H160; @@ -27,31 +27,3 @@ fn ethereum_precompiled_addresses_are_precompile() { assert!(is_precompile(H160::from_str("0x8000000000000000000000000000000000000001").unwrap()) .unwrap()); } - -#[test] -fn check_for_erc721_addresses() { - assert!(!is_erc721_address( - H160::from_str("0x1000000000000000000000000000000000000001").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0x8000000000000000000000000000000000000000").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0x8000000000000000000000000000000000000001").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0x8000000000000000000000000000000000000002").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0x8000000000000000000000000000000000000003").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0x80000000000000000000000000000000ffffffff").unwrap() - )); - assert!(!is_erc721_address( - H160::from_str("0x7fffffffffffffffffffffffffffffffffffffff").unwrap() - )); - assert!(is_erc721_address( - H160::from_str("0xffffffffffffffffffffffffffffffffffffffff").unwrap() - )); -} From daf291099318c1f8f7b0d2b0c443cb287b436e99 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 11:05:33 +0200 Subject: [PATCH 038/114] collection address prefix changed --- pallets/living-assets-ownership/src/lib.rs | 39 ++++++++++---- pallets/living-assets-ownership/src/tests.rs | 54 +++++++++----------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index d1d30fe3..16a07c4b 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -98,22 +98,41 @@ pub mod pallet { } } +/// `ASSET_PRECOMPILE_ADDRESS_PREFIX` is a predefined prefix used to identify collection addresses. +/// +/// All addresses that start with this prefix are considered as collection addresses. +/// Since `CollectionId` is represented as a `u64`, it leaves these bits free to be +/// utilized for such a prefix. +/// +/// Usage of this prefix provides a consistent and recognizable pattern for distinguishing +/// collection addresses from other types of addresses in the system. +pub const ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[0xff; 12]; + +#[derive(Debug, PartialEq)] +pub enum CollectionError { + InvalidPrefix, + SliceLengthMismatch, +} + pub fn collection_id_to_address(collection_id: CollectionId) -> H160 { - let mut address = H160::from_low_u64_be(collection_id); - address.0[0] |= 0x80; // Set the first bit to 1 - address + let mut bytes = [0u8; 20]; + bytes[12..20].copy_from_slice(&collection_id.to_be_bytes()); + for (i, byte) in ASSET_PRECOMPILE_ADDRESS_PREFIX.iter().enumerate() { + bytes[i] = *byte; + } + H160(bytes) } -pub fn address_to_collection_id(address: H160) -> CollectionId { - let bytes: [u8; 20] = address.into(); - let id_bytes: [u8; 8] = bytes[12..20].try_into().expect("Slice length doesn't match"); - CollectionId::from_be_bytes(id_bytes) +pub fn address_to_collection_id(address: H160) -> Result { + if &address.0[0..12] != ASSET_PRECOMPILE_ADDRESS_PREFIX { + return Err(CollectionError::InvalidPrefix); + } + let id_bytes: [u8; 8] = address.0[12..20].try_into().unwrap(); + Ok(CollectionId::from_be_bytes(id_bytes)) } pub fn is_collection_address(address: H160) -> bool { - let first_byte = address.0[0]; - // Check if the first bit is set to 1 - first_byte & 0x80 == 0x80 + &address.to_fixed_bytes()[0..12] == ASSET_PRECOMPILE_ADDRESS_PREFIX } #[cfg(test)] diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 4fc0d329..8e050f7a 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -4,7 +4,7 @@ use crate::{ address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, traits::{CollectionManager, Erc721}, - Event, + CollectionError, CollectionId, Event, }; use frame_support::assert_ok; use sp_core::H160; @@ -141,41 +141,35 @@ fn erc721_owner_of_asset_of_collection() { #[test] fn test_collection_id_to_address() { let collection_id: u64 = 5; - let expected_address = H160::from_str("8000000000000000000000000000000000000005").unwrap(); + let expected_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); assert_eq!(collection_id_to_address(collection_id), expected_address); } #[test] -fn test_address_to_collection_id() { +fn invalid_collection_address_should_error() { let address = H160::from_str("8000000000000000000000000000000000000005").unwrap(); - let collection_it = address_to_collection_id(address); - assert_eq!(collection_it, 5); + let error = address_to_collection_id(address).unwrap_err(); + assert_eq!(error, CollectionError::InvalidPrefix); } #[test] -fn check_for_erc721_addresses() { - assert!(!is_collection_address( - H160::from_str("0x1000000000000000000000000000000000000001").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0x8000000000000000000000000000000000000000").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0x8000000000000000000000000000000000000001").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0x8000000000000000000000000000000000000002").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0x8000000000000000000000000000000000000003").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0x80000000000000000000000000000000ffffffff").unwrap() - )); - assert!(!is_collection_address( - H160::from_str("0x7fffffffffffffffffffffffffffffffffffffff").unwrap() - )); - assert!(is_collection_address( - H160::from_str("0xffffffffffffffffffffffffffffffffffffffff").unwrap() - )); +fn valid_collection_address_should_return_collection_id() { + let address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); + let collection_id = address_to_collection_id(address).unwrap(); + assert_eq!(collection_id, 5); +} + +#[test] +fn test_is_collection_address_valid() { + let collection_id: CollectionId = 1234567890; + let address = collection_id_to_address(collection_id); + + assert!(is_collection_address(address)); +} + +#[test] +fn test_is_collection_address_invalid() { + let invalid_address = H160([0u8; 20]); + + assert!(!is_collection_address(invalid_address)); } From bd47de2285e25456a1fabcdc36ce5ea966e9eb4e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 11:40:10 +0200 Subject: [PATCH 039/114] test passing --- precompile/erc721/src/lib.rs | 11 ++++++++++- precompile/erc721/src/tests.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 928fb02e..67557a26 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -55,7 +55,16 @@ where let asset_id: U256 = input.read()?; // collection id is encoded into the contract address - let collection_id = address_to_collection_id(handle.code_address()); + let collection_id = match address_to_collection_id(handle.code_address()) { + Ok(collection_id) => collection_id, + Err(_) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed( + "invalid collection address", + )), + }) + }, + }; match AssetManager::owner_of(collection_id, asset_id) { Ok(owner) => Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())), Err(err) => Err(PrecompileFailure::Error { diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 79d07ab9..45c880ce 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use super::*; -use evm::ExitError; +use evm::{ExitError, ExitRevert}; use fp_evm::PrecompileFailure; use pallet_living_assets_ownership::CollectionId; use precompile_utils::testing::create_mock_handle_from_input; @@ -27,6 +27,7 @@ fn owner_of_asset_should_return_an_address() { hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") .unwrap(); let mut handle = create_mock_handle_from_input(owner_of_asset_4); + handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( @@ -36,13 +37,14 @@ fn owner_of_asset_should_return_an_address() { } #[test] -fn owner_of_if_fails_returns_error() { +fn if_mock_fails_should_return_the_error() { impl_precompile_mock_simple!(Mock, Err("spaghetti error")); let owner_of_asset_4 = hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") .unwrap(); let mut handle = create_mock_handle_from_input(owner_of_asset_4); + handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); assert_eq!( @@ -53,6 +55,27 @@ fn owner_of_if_fails_returns_error() { ); } +#[test] +fn invalid_contract_address_should_error() { + impl_precompile_mock_simple!(Mock, Ok(H160::zero())); + + let mut handle = create_mock_handle_from_input(Vec::new()); + handle.code_address = H160::zero(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err(), + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: vec![ + 116, 114, 105, 101, 100, 32, 116, 111, 32, 112, 97, 114, 115, 101, 32, 115, 101, + 108, 101, 99, 116, 111, 114, 32, 111, 117, 116, 32, 111, 102, 32, 98, 111, 117, + 110, 100, 115 + ] + } + ); +} + // #[test] // fn owner_of() { // impl_precompile_mock!(Mock, |collection_id, asset_id| { From 907ca7e33c80ffe8811592467477a103143baafc Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 11:53:27 +0200 Subject: [PATCH 040/114] test passing --- precompile/erc721/src/lib.rs | 16 ++++--------- precompile/erc721/src/tests.rs | 42 ++++++---------------------------- 2 files changed, 11 insertions(+), 47 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 67557a26..26653cb0 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,11 +1,11 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -use fp_evm::{ExitError, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{address_to_collection_id, traits::Erc721}; use parity_scale_codec::Encode; use precompile_utils::{ - succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, + revert, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, }; use sp_core::U256; @@ -57,19 +57,11 @@ where // collection id is encoded into the contract address let collection_id = match address_to_collection_id(handle.code_address()) { Ok(collection_id) => collection_id, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed( - "invalid collection address", - )), - }) - }, + Err(_) => return Err(revert("invalid collection address")), }; match AssetManager::owner_of(collection_id, asset_id) { Ok(owner) => Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())), - Err(err) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed(err)), - }), + Err(err) => Err(revert(err)), } }, } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 45c880ce..a1092d3b 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,8 +1,6 @@ use core::str::FromStr; use super::*; -use evm::{ExitError, ExitRevert}; -use fp_evm::PrecompileFailure; use pallet_living_assets_ownership::CollectionId; use precompile_utils::testing::create_mock_handle_from_input; use sp_core::{H160, U256}; @@ -31,8 +29,11 @@ fn owner_of_asset_should_return_an_address() { let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( - hex::encode(result.unwrap().output), - "000000000000000000000000ff00000000000000000000000000000012345678", + result.unwrap(), + succeed( + hex::decode("000000000000000000000000ff00000000000000000000000000000012345678") + .unwrap() + ), ); } @@ -47,12 +48,7 @@ fn if_mock_fails_should_return_the_error() { handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!( - result.unwrap_err(), - PrecompileFailure::Error { - exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed("spaghetti error")) - } - ); + assert_eq!(result.unwrap_err(), revert("spaghetti error"),); } #[test] @@ -63,33 +59,9 @@ fn invalid_contract_address_should_error() { handle.code_address = H160::zero(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!( - result.unwrap_err(), - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: vec![ - 116, 114, 105, 101, 100, 32, 116, 111, 32, 112, 97, 114, 115, 101, 32, 115, 101, - 108, 101, 99, 116, 111, 114, 32, 111, 117, 116, 32, 111, 102, 32, 98, 111, 117, - 110, 100, 115 - ] - } - ); + assert_eq!(result.unwrap_err(), revert("tried to parse selector out of bounds"),); } -// #[test] -// fn owner_of() { -// impl_precompile_mock!(Mock, |collection_id, asset_id| { -// assert_eq!(collection_id, 0); -// assert_eq!(asset_id, U256::from("0x1234")); -// Some(H160::from(2)) -// }); - -// let owner_of_1234 = "6352211e0000000000000000000000000000000000000000000000000000000000001234"; -// let mut handle = create_mock_handle_from_input(owner_of_1234); -// let result = Mock::execute(&mut handle); -// assert!(result.is_ok()); -// assert_eq!(result.unwrap().output, H160::zero().encode()); -// } mod helpers { /// Macro to define a precompile mock with custom closures for testing. /// From 10557aa313c8c7f3aac5a729b0a9e82b6a269a96 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:03:43 +0200 Subject: [PATCH 041/114] refactoring tests --- precompile/living-assets/src/lib.rs | 10 +++---- precompile/living-assets/src/tests.rs | 39 +++++++++------------------ 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index a7956049..ab1242b4 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -1,14 +1,14 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -use fp_evm::{ExitError, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{ collection_id_to_address, traits::CollectionManager, CollectionId, }; use parity_scale_codec::Encode; use precompile_utils::{ - keccak256, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, LogExt, LogsBuilder, - PrecompileHandleExt, + keccak256, revert, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, LogExt, + LogsBuilder, PrecompileHandleExt, }; use sp_runtime::SaturatedConversion; @@ -64,9 +64,7 @@ where Ok(succeed(EvmDataWriter::new().write(Address(collection_address)).build())) }, - Err(err) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed(err)), - }), + Err(err) => Err(revert(err)), } }, } diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index cd16203a..3797d79f 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -4,8 +4,10 @@ #![allow(clippy::redundant_closure_call)] use super::*; -use evm::ExitRevert; -use precompile_utils::testing::{create_mock_handle, create_mock_handle_from_input}; +use precompile_utils::{ + revert, succeed, + testing::{create_mock_handle, create_mock_handle_from_input}, +}; use sp_core::H160; use sp_std::vec::Vec; @@ -33,12 +35,7 @@ fn failing_create_collection_should_return_error() { let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); let result = Mock::execute(&mut handle); - assert_eq!( - result.unwrap_err(), - PrecompileFailure::Error { - exit_status: ExitError::Other(sp_std::borrow::Cow::Borrowed("spaghetti code")) - } - ); + assert_eq!(result.unwrap_err(), revert("spaghetti code")); } #[test] @@ -49,8 +46,11 @@ fn create_collection_should_return_address() { let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( - hex::encode(result.unwrap().output), - "0000000000000000000000008000000000000000000000000000000000000005" + result.unwrap(), + succeed( + hex::decode("000000000000000000000000ffffffffffffffffffffffff0000000000000005") + .unwrap() + ) ); } @@ -68,7 +68,7 @@ fn create_collection_should_generate_log() { assert_eq!(logs[0].topics[0], SELECTOR_LOG_CREATE_COLLECTION.into()); assert_eq!( hex::encode(logs[0].topics[1]), - "000000000000000000000000800000000000000000000000000000000000ffff" + "000000000000000000000000ffffffffffffffffffffffff000000000000ffff" ); assert_eq!(logs[0].data, Vec::::new()); } @@ -80,13 +80,7 @@ fn create_collection_on_mock_with_nonzero_value_fails() { create_mock_handle(hex::decode(CREATE_COLLECTION).unwrap(), 0, 1, H160::zero()); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!( - result.unwrap_err(), - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: "function is not payable".to_string().into_bytes() - } - ); + assert_eq!(result.unwrap_err(), revert("function is not payable")); } #[test] @@ -120,14 +114,7 @@ fn call_unexistent_selector_should_fail() { .unwrap(); let mut handle = create_mock_handle_from_input(input); let result = Mock::execute(&mut handle); - assert_eq!( - result.unwrap_err(), - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: [117, 110, 107, 110, 111, 119, 110, 32, 115, 101, 108, 101, 99, 116, 111, 114] - .to_vec() - } - ); + assert_eq!(result.unwrap_err(), revert("unknown selector")); } mod helpers { From 6d38abe3c180ee0c456ece06f15250d954638d36 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:14:01 +0200 Subject: [PATCH 042/114] do not panic the runtime --- precompile/erc721/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 26653cb0..3d66b5c0 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -46,7 +46,7 @@ where match selector { Action::TockenURI => { - todo!() + Err(revert("not implemented")) }, Action::OwnerOf => { let mut input = handle.read_input()?; From daef0511faec53565e75f49e325b89a34917f831 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:15:19 +0200 Subject: [PATCH 043/114] fmt --- precompile/erc721/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 3d66b5c0..aa99de59 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -45,9 +45,7 @@ where })?; match selector { - Action::TockenURI => { - Err(revert("not implemented")) - }, + Action::TockenURI => Err(revert("not implemented")), Action::OwnerOf => { let mut input = handle.read_input()?; input.expect_arguments(1)?; From f71eef7bd89ef99ce6783f2e8d30f0eb8441cba3 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:19:35 +0200 Subject: [PATCH 044/114] solidity tokenId -> _tokenId --- precompile/erc721/contracts/IERC721.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol index 2325486a..c12dcb51 100644 --- a/precompile/erc721/contracts/IERC721.sol +++ b/precompile/erc721/contracts/IERC721.sol @@ -6,7 +6,7 @@ interface IERC721 { /** * @dev See {IERC721Metadata-tokenURI}. */ - function tokenURI(uint256 tokenId) external view returns (string memory); + function tokenURI(uint256 _tokenId) external view returns (string memory); function ownerOf(uint256 _tokenId) external view returns (address); } From 15123db506ede82d227f9366f51528eaf0fd381e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:20:51 +0200 Subject: [PATCH 045/114] erc721 functions are views --- precompile/erc721/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index aa99de59..9d9708ea 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -40,8 +40,8 @@ where let selector = handle.read_selector()?; handle.check_function_modifier(match selector { - Action::TockenURI => FunctionModifier::NonPayable, - Action::OwnerOf => FunctionModifier::NonPayable, + Action::TockenURI => FunctionModifier::View, + Action::OwnerOf => FunctionModifier::View, })?; match selector { From 4b0ec80a8778246ee00ce678ee2f8f5ef3c17bbb Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:30:51 +0200 Subject: [PATCH 046/114] update docs --- precompile/erc721/src/tests.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index a1092d3b..07fc1b4a 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -67,21 +67,19 @@ mod helpers { /// /// This macro creates mock implementations of the `Erc721` trait, /// allowing you to test how your code interacts with the precompiled contracts. - /// You can define custom closures for the create_collection and owner_of_collection functions. + /// You can define a custom closure for the owner_of function. /// /// # Arguments /// /// * `$name`: An identifier to name the precompile mock type. - /// * `$create_collection_result`: A closure that takes `collection_id` and `who` and returns a `DispatchResult`. - /// * `$owner_of_collection_result`: A closure that takes `collection_id` and returns an `Option`. + /// * `$owner_of_collection`: A closure that takes `collection_id` and `asset_id` and returns a `Result`. /// /// # Example /// /// ``` /// impl_precompile_mock!( /// MyMock, - /// |who| { Ok(0) }, - /// |collection_id| { Some(H160::zero()) } + /// |collection_id, asset_id| { Ok(AccountId::default()) } /// ); /// ``` #[macro_export] @@ -106,17 +104,17 @@ mod helpers { /// /// This macro creates mock implementations of the `Erc721` trait, /// allowing you to test how your code interacts with the precompiled contracts. - /// The mock type is named `Mock`, and the implementation uses the provided expressions. + /// The mock type is named based on the provided identifier, and the implementation uses the provided expression. /// /// # Arguments /// - /// * `$create_collection_result`: An expression that evaluates to a `DispatchResult`. - /// * `$owner_of_collection_result`: An expression that evaluates to an `Option`. + /// * `$name`: An identifier to name the precompile mock type. + /// * `$owner_of_collection`: An expression that evaluates to a `Result`. /// /// # Example /// /// ``` - /// impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::zero())); + /// impl_precompile_mock_simple!(Mock, Ok(AccountId::default())); /// ``` #[macro_export] macro_rules! impl_precompile_mock_simple { From df5932461b1ba22f78279ba6e000e60de7643a59 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:49:23 +0200 Subject: [PATCH 047/114] sp-core in std , removed duplicate function --- pallets/living-assets-ownership/Cargo.toml | 3 ++- runtime/src/precompiles/mod.rs | 7 +------ runtime/src/precompiles/test.rs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index ce931860..10def798 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -45,7 +45,8 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", - "sp-arithmetic/std" + "sp-arithmetic/std", + "sp-core/std", ] try-runtime = [ "frame-system/try-runtime", diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index ad3bc9eb..399b5e2c 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -11,6 +11,7 @@ use pallet_evm_erc721::Erc721Precompile; use pallet_evm_living_assets_ownership::CollectionManagerPrecompile; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use pallet_living_assets_ownership::is_collection_address; use crate::AccountId; @@ -77,12 +78,6 @@ fn hash(a: u64) -> H160 { H160::from_low_u64_be(a) } -fn is_collection_address(address: H160) -> bool { - let first_byte = address.0[0]; - // Check if the first bit is set to 1 - first_byte & 0x80 == 0x80 -} - #[cfg(test)] mod mock; diff --git a/runtime/src/precompiles/test.rs b/runtime/src/precompiles/test.rs index ce6396d1..1f9d25da 100644 --- a/runtime/src/precompiles/test.rs +++ b/runtime/src/precompiles/test.rs @@ -24,6 +24,6 @@ fn ethereum_precompiled_addresses_are_precompile() { assert!(is_precompile(hash(4)).unwrap()); assert!(is_precompile(hash(5)).unwrap()); assert!(is_precompile(hash(1026)).unwrap()); - assert!(is_precompile(H160::from_str("0x8000000000000000000000000000000000000001").unwrap()) + assert!(is_precompile(H160::from_str("0xffffffffffffffffffffffff0000000000000005").unwrap()) .unwrap()); } From 2fbd14494b1b5a99ba2e2ceaf9395cedbc802f1a Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:56:58 +0200 Subject: [PATCH 048/114] documentaetion --- pallets/living-assets-ownership/src/lib.rs | 39 +++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 16a07c4b..b0dea1f3 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -108,12 +108,25 @@ pub mod pallet { /// collection addresses from other types of addresses in the system. pub const ASSET_PRECOMPILE_ADDRESS_PREFIX: &[u8] = &[0xff; 12]; +/// Enum representing possible errors related to collections. #[derive(Debug, PartialEq)] pub enum CollectionError { + /// Error indicating that the provided address does not have the correct prefix. InvalidPrefix, - SliceLengthMismatch, } +/// Converts a `CollectionId` into an `H160` address format. +/// +/// This function takes the given `CollectionId`, which is assumed to be a `u64`, +/// and maps it into an `H160` address, prepending it with the `ASSET_PRECOMPILE_ADDRESS_PREFIX`. +/// +/// # Arguments +/// +/// * `collection_id`: The ID of the collection to be converted. +/// +/// # Returns +/// +/// * An `H160` representation of the collection ID. pub fn collection_id_to_address(collection_id: CollectionId) -> H160 { let mut bytes = [0u8; 20]; bytes[12..20].copy_from_slice(&collection_id.to_be_bytes()); @@ -123,6 +136,18 @@ pub fn collection_id_to_address(collection_id: CollectionId) -> H160 { H160(bytes) } +/// Converts an `H160` address into a `CollectionId` format. +/// +/// This function takes the given `H160` address, checks for the correct prefix, and extracts +/// the `CollectionId` from it. If the prefix is incorrect, it returns a `CollectionError::InvalidPrefix` error. +/// +/// # Arguments +/// +/// * `address`: The `H160` address to be converted. +/// +/// # Returns +/// +/// * A `Result` which is either the `CollectionId` or an error indicating the address is invalid. pub fn address_to_collection_id(address: H160) -> Result { if &address.0[0..12] != ASSET_PRECOMPILE_ADDRESS_PREFIX { return Err(CollectionError::InvalidPrefix); @@ -131,6 +156,18 @@ pub fn address_to_collection_id(address: H160) -> Result bool { &address.to_fixed_bytes()[0..12] == ASSET_PRECOMPILE_ADDRESS_PREFIX } From 96d737494ea114eaca438e91cc7b153074168388 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 12:59:26 +0200 Subject: [PATCH 049/114] using compilator type ifer --- pallets/living-assets-ownership/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 8e050f7a..a6e34157 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -4,7 +4,7 @@ use crate::{ address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, traits::{CollectionManager, Erc721}, - CollectionError, CollectionId, Event, + CollectionError, Event, }; use frame_support::assert_ok; use sp_core::H160; @@ -140,7 +140,7 @@ fn erc721_owner_of_asset_of_collection() { #[test] fn test_collection_id_to_address() { - let collection_id: u64 = 5; + let collection_id = 5; let expected_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); assert_eq!(collection_id_to_address(collection_id), expected_address); } @@ -161,7 +161,7 @@ fn valid_collection_address_should_return_collection_id() { #[test] fn test_is_collection_address_valid() { - let collection_id: CollectionId = 1234567890; + let collection_id = 1234567890; let address = collection_id_to_address(collection_id); assert!(is_collection_address(address)); From 5bf67b02a3c44d55c09c58d5716429e7c6728b67 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 14:47:45 +0200 Subject: [PATCH 050/114] compiler infer the size of the array --- pallets/living-assets-ownership/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index b0dea1f3..57cfc62a 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -152,7 +152,7 @@ pub fn address_to_collection_id(address: H160) -> Result Date: Tue, 8 Aug 2023 13:55:11 +0100 Subject: [PATCH 051/114] added to std feature --- runtime/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 50784833..0b2d436c 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -156,6 +156,7 @@ std = [ "pallet-base-fee/std", "pallet-ethereum/std", "pallet-evm/std", + "pallet-evm-erc721/std", "pallet-evm-chain-id/std", "pallet-evm-precompile-modexp/std", "pallet-evm-precompile-simple/std", From 9e1bf8dacf06b273ebb481b596cc0844a8f3e89b Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 14:03:03 +0100 Subject: [PATCH 052/114] rename variable --- precompile/living-assets/src/tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 3797d79f..a4ac1b8e 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -108,11 +108,10 @@ fn create_collection_assign_collection_to_caller() { fn call_unexistent_selector_should_fail() { impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::from_low_u64_be(0x1234))); - // unexistent selector - let input = + let unexistent_selector = hex::decode("fb24ae530000000000000000000000000000000000000000000000000000000000000000") .unwrap(); - let mut handle = create_mock_handle_from_input(input); + let mut handle = create_mock_handle_from_input(unexistent_selector); let result = Mock::execute(&mut handle); assert_eq!(result.unwrap_err(), revert("unknown selector")); } From bee480658ae2355536bde5200cec80deff7502d2 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Tue, 8 Aug 2023 14:07:52 +0100 Subject: [PATCH 053/114] trait documentation --- pallets/living-assets-ownership/src/traits.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 2e1e8d86..63c38b27 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -28,6 +28,22 @@ pub trait CollectionManager { fn create_collection(owner: AccountId) -> Result; } +/// `Erc721` Trait +/// +/// This trait provides an interface for handling ERC721 tokens, a standard for non-fungible tokens on the blockchain. pub trait Erc721 { - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; + + /// Retrieves the owner of a specific asset in a collection. + /// + /// # Parameters + /// + /// * `collection_id`: An identifier for the collection to which the asset belongs. + /// * `asset_id`: The unique identifier for the asset within the specified collection. + /// + /// # Returns + /// + /// * A `Result` which is: + /// - `Ok(H160)`: Returns the Ethereum address (`H160`) of the owner of the asset. + /// - `Err(&'static str)`: Returns an error message if the asset owner could not be determined. + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; } From 874bfae4e18763f5982161ebed3f8eaedb6ec685 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 11:50:08 +0200 Subject: [PATCH 054/114] using pallet errors --- pallets/living-assets-ownership/src/functions.rs | 2 +- pallets/living-assets-ownership/src/lib.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index abf2b028..9065d9cf 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -5,7 +5,7 @@ use sp_core::{H160, U256}; impl Pallet { /// See [Self::create_collection] - pub fn do_create_collection(who: T::AccountId) -> Result { + pub fn do_create_collection(who: T::AccountId) -> Result> { // Retrieve the current collection count to use as the new collection's ID let collection_id = Self::collection_counter(); diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 57cfc62a..79518f3c 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -84,7 +84,10 @@ pub mod pallet { } fn create_collection(owner: T::AccountId) -> Result { - Self::do_create_collection(owner) + match Self::do_create_collection(owner) { + Ok(collection_id) => Ok(collection_id), + Err(err) => Err(err.into()), + } } } From 1322a4fa2c200ebe39723a01b92a01af9f05b171 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 12:07:23 +0200 Subject: [PATCH 055/114] Erc721Error created --- pallets/living-assets-ownership/src/lib.rs | 7 +++++-- pallets/living-assets-ownership/src/tests.rs | 10 ++++------ pallets/living-assets-ownership/src/traits.rs | 8 +++++++- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 79518f3c..60fb7d96 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -92,10 +92,13 @@ pub mod pallet { } impl traits::Erc721 for Pallet { - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { + fn owner_of( + collection_id: CollectionId, + asset_id: U256, + ) -> Result { match OwnerOfCollection::::get(collection_id) { Some(_) => Ok(convert_asset_id_to_owner(asset_id)), - None => Err("Collection does not exist"), + None => Err(traits::Erc721Error::UnexistentCollection), } } } diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index a6e34157..824f3ba7 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -3,10 +3,10 @@ use core::str::FromStr; use crate::{ address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, - traits::{CollectionManager, Erc721}, + traits::{self, CollectionManager, Erc721}, CollectionError, Event, }; -use frame_support::assert_ok; +use frame_support::{assert_err, assert_ok}; use sp_core::H160; type AccountId = ::AccountId; @@ -119,10 +119,8 @@ fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { #[test] fn erc721_owner_of_asset_of_unexistent_collection() { new_test_ext().execute_with(|| { - assert_eq!( - ::owner_of(0, 2.into()), - Err("Collection does not exist") - ); + let result = ::owner_of(0, 2.into()); + assert_err!(result, traits::Erc721Error::UnexistentCollection); }); } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index c5db2984..2ccc8414 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -44,5 +44,11 @@ pub trait Erc721 { /// * A `Result` which is: /// - `Ok(H160)`: Returns the Ethereum address (`H160`) of the owner of the asset. /// - `Err(&'static str)`: Returns an error message if the asset owner could not be determined. - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; +} + +#[derive(Debug, PartialEq)] +pub enum Erc721Error { + UnexistentCollection, + Unknown, } From 1679acb834f3ba9e9764e4f282c5f2cda5a5b0d2 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 12:18:56 +0200 Subject: [PATCH 056/114] Erc721Error fix compilation --- pallets/living-assets-ownership/src/traits.rs | 9 ++++++++- precompile/erc721/src/tests.rs | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 2ccc8414..5d96edb5 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -50,5 +50,12 @@ pub trait Erc721 { #[derive(Debug, PartialEq)] pub enum Erc721Error { UnexistentCollection, - Unknown, +} + +impl AsRef<[u8]> for Erc721Error { + fn as_ref(&self) -> &[u8] { + match self { + Erc721Error::UnexistentCollection => b"UnexistentCollection", + } + } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 07fc1b4a..b9a7b1e2 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use super::*; -use pallet_living_assets_ownership::CollectionId; +use pallet_living_assets_ownership::{traits::Erc721Error, CollectionId}; use precompile_utils::testing::create_mock_handle_from_input; use sp_core::{H160, U256}; @@ -39,7 +39,7 @@ fn owner_of_asset_should_return_an_address() { #[test] fn if_mock_fails_should_return_the_error() { - impl_precompile_mock_simple!(Mock, Err("spaghetti error")); + impl_precompile_mock_simple!(Mock, Err(Erc721Error::UnexistentCollection)); let owner_of_asset_4 = hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") @@ -48,7 +48,7 @@ fn if_mock_fails_should_return_the_error() { handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("spaghetti error"),); + assert_eq!(result.unwrap_err(), revert(Erc721Error::UnexistentCollection)); } #[test] @@ -91,7 +91,7 @@ mod helpers { fn owner_of( collectio_id: CollectionId, asset_id: U256, - ) -> Result { + ) -> Result { ($owner_of_collection)(collectio_id, asset_id) } } From e8e789f4a0a2aa6b5aed6be93984b24ba57c81ad Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 15:08:45 +0200 Subject: [PATCH 057/114] tests green --- pallets/living-assets-ownership/src/lib.rs | 14 ++++++++++++-- pallets/living-assets-ownership/src/traits.rs | 18 +++++++++++++++++- precompile/living-assets/src/tests.rs | 13 ++++++++++--- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 60fb7d96..f5b498c3 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -83,10 +83,20 @@ pub mod pallet { OwnerOfCollection::::get(collection_id) } - fn create_collection(owner: T::AccountId) -> Result { + fn create_collection( + owner: T::AccountId, + ) -> Result { match Self::do_create_collection(owner) { Ok(collection_id) => Ok(collection_id), - Err(err) => Err(err.into()), + Err(err) => match err { + Error::CollectionAlreadyExists => { + Err(traits::CollectionManagerError::CollectionAlreadyExists) + }, + Error::CollectionIdOverflow => { + Err(traits::CollectionManagerError::CollectionIdOverflow) + }, + _ => Err(traits::CollectionManagerError::UnknownError), + }, } } } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 5d96edb5..daba7170 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -25,9 +25,25 @@ pub trait CollectionManager { fn owner_of_collection(collection_id: CollectionId) -> Option; /// Create collection - fn create_collection(owner: AccountId) -> Result; + fn create_collection(owner: AccountId) -> Result; } +#[derive(Debug, PartialEq)] +pub enum CollectionManagerError { + CollectionAlreadyExists, + CollectionIdOverflow, + UnknownError, +} + +impl AsRef<[u8]> for CollectionManagerError { + fn as_ref(&self) -> &[u8] { + match self { + CollectionManagerError::CollectionAlreadyExists => b"CollectionAlreadyExists", + CollectionManagerError::CollectionIdOverflow => b"CollectionIdOverflow", + CollectionManagerError::UnknownError => b"UnknownError", + } + } +} /// `Erc721` Trait /// /// This trait provides an interface for handling ERC721 tokens, a standard for non-fungible tokens on the blockchain. diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index a4ac1b8e..6d404a1d 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -4,6 +4,7 @@ #![allow(clippy::redundant_closure_call)] use super::*; +use pallet_living_assets_ownership::traits::CollectionManagerError; use precompile_utils::{ revert, succeed, testing::{create_mock_handle, create_mock_handle_from_input}, @@ -31,11 +32,15 @@ fn check_log_selectors() { #[test] fn failing_create_collection_should_return_error() { - impl_precompile_mock_simple!(Mock, Err("spaghetti code"), Some(H160::zero())); + impl_precompile_mock_simple!( + Mock, + Err(CollectionManagerError::CollectionAlreadyExists), + Some(H160::zero()) + ); let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); let result = Mock::execute(&mut handle); - assert_eq!(result.unwrap_err(), revert("spaghetti code")); + assert_eq!(result.unwrap_err(), revert(CollectionManagerError::CollectionAlreadyExists)); } #[test] @@ -142,7 +147,9 @@ mod helpers { impl pallet_living_assets_ownership::traits::CollectionManager for CollectionManagerMock { - fn create_collection(owner: AccountId) -> Result { + fn create_collection( + owner: AccountId, + ) -> Result { ($create_collection_result)(owner) } From a2763f75e45828f3d2d0a3acb99f94c1073ec88a Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 15:09:44 +0200 Subject: [PATCH 058/114] revise docuemntation --- pallets/living-assets-ownership/src/traits.rs | 69 ++++++++++++------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index daba7170..826f2482 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,33 +1,44 @@ use sp_core::{H160, U256}; - use crate::CollectionId; -/// The `CollectionManager` trait provides an interface for managing collections in a -/// decentralized and non-fungible asset management system. This system allows for the creation of -/// collections, each of which can be owned by a unique `AccountId`. +/// The `CollectionManager` trait provides an interface for managing collections in a decentralized system. /// -/// A collection in this context can be thought of as a container for non-fungible assets. -/// Each collection has an associated `collection_id` which is a unique identifier for the collection -/// and can be used to retrieve the owner of the collection. +/// A collection is a container for non-fungible assets, uniquely identified by a `collection_id`. The system allows +/// for the creation and ownership management of these collections. /// /// # Methods /// -/// - `owner_of_collection(collection_id: CollectionId) -> Option`: This method retrieves the owner -/// of a collection given its `collection_id`. If no collection exists with the provided `collection_id`, -/// the method returns `None`. -/// -/// - `create_collection(owner: AccountId) -> Result`: This method creates a -/// new collection and assigns ownership to the provided `AccountId`. The method returns the `collection_id` -/// of the newly created collection. -/// +/// - `owner_of_collection`: Retrieve the owner of a specified collection. +/// - `create_collection`: Create a new collection and assign it to an owner. pub trait CollectionManager { - /// Get owner of collection + /// Retrieves the owner of the specified collection. + /// + /// # Arguments + /// + /// * `collection_id` - The unique identifier of the collection. + /// + /// # Returns + /// + /// The account ID of the collection's owner or `None` if the collection doesn't exist. fn owner_of_collection(collection_id: CollectionId) -> Option; - /// Create collection + /// Creates a new collection and assigns it to the specified owner. + /// + /// # Arguments + /// + /// * `owner` - The account ID of the new collection's owner. + /// + /// # Returns + /// + /// A result containing the `collection_id` of the newly created collection or an error. fn create_collection(owner: AccountId) -> Result; } +/// Errors that can occur when managing collections. +/// +/// - `CollectionAlreadyExists`: A collection with the same ID already exists. +/// - `CollectionIdOverflow`: The ID for the new collection would overflow. +/// - `UnknownError`: An unspecified error occurred. #[derive(Debug, PartialEq)] pub enum CollectionManagerError { CollectionAlreadyExists, @@ -44,25 +55,31 @@ impl AsRef<[u8]> for CollectionManagerError { } } } -/// `Erc721` Trait + +/// The `Erc721` trait provides an interface for handling ERC721 tokens in a blockchain environment. /// -/// This trait provides an interface for handling ERC721 tokens, a standard for non-fungible tokens on the blockchain. +/// ERC721 tokens are a standard for representing ownership of unique items on the Ethereum blockchain. +/// +/// # Methods +/// +/// - `owner_of`: Retrieve the owner of a specific asset within a collection. pub trait Erc721 { - /// Retrieves the owner of a specific asset in a collection. + /// Retrieves the owner of a specific asset within the specified collection. /// - /// # Parameters + /// # Arguments /// - /// * `collection_id`: An identifier for the collection to which the asset belongs. - /// * `asset_id`: The unique identifier for the asset within the specified collection. + /// * `collection_id` - The unique identifier for the collection. + /// * `asset_id` - The unique identifier for the asset within the collection. /// /// # Returns /// - /// * A `Result` which is: - /// - `Ok(H160)`: Returns the Ethereum address (`H160`) of the owner of the asset. - /// - `Err(&'static str)`: Returns an error message if the asset owner could not be determined. + /// The Ethereum address (`H160`) of the asset's owner or an error. fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; } +/// Errors that can occur when interacting with ERC721 tokens. +/// +/// - `UnexistentCollection`: The specified collection does not exist. #[derive(Debug, PartialEq)] pub enum Erc721Error { UnexistentCollection, From 33083243918e798cf7bf7e0c2944464292ffe830 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 15:46:37 +0200 Subject: [PATCH 059/114] fmt --- pallets/living-assets-ownership/src/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 826f2482..f958f8be 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,5 +1,5 @@ -use sp_core::{H160, U256}; use crate::CollectionId; +use sp_core::{H160, U256}; /// The `CollectionManager` trait provides an interface for managing collections in a decentralized system. /// From 790361eb4bf9333e77074f08ec2551b9df8b914c Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 15:48:10 +0200 Subject: [PATCH 060/114] better name of constant --- precompile/living-assets/src/tests.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 6cd177b1..a9109207 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -15,7 +15,7 @@ use sp_std::vec::Vec; type AccountId = H160; type AddressMapping = pallet_evm::IdentityAddressMapping; -const CREATE_COLLECTION: &str = "0x059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; +const CREATE_COLLECTION_WITH_URI: &str = "0x059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; #[test] fn check_selectors() { @@ -38,7 +38,7 @@ fn failing_create_collection_should_return_error() { Some(H160::zero()) ); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); assert_eq!(result.unwrap_err(), revert(CollectionManagerError::CollectionAlreadyExists)); } @@ -47,7 +47,7 @@ fn failing_create_collection_should_return_error() { fn create_collection_should_return_address() { impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( @@ -63,7 +63,7 @@ fn create_collection_should_return_address() { fn create_collection_should_generate_log() { impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION).unwrap()); + let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); let logs = handle.logs; @@ -82,7 +82,7 @@ fn create_collection_should_generate_log() { fn create_collection_on_mock_with_nonzero_value_fails() { impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); let mut handle = - create_mock_handle(hex::decode(CREATE_COLLECTION).unwrap(), 0, 1, H160::zero()); + create_mock_handle(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap(), 0, 1, H160::zero()); let result = Mock::execute(&mut handle); assert!(result.is_err()); assert_eq!(result.unwrap_err(), revert("function is not payable")); @@ -100,7 +100,7 @@ fn create_collection_assign_collection_to_caller() { ); let mut handle = create_mock_handle( - hex::decode(CREATE_COLLECTION).unwrap(), + hex::decode(CREATE_COLLECTION_WITH_URI).unwrap(), 0, 0, H160::from_low_u64_be(0x1234), From dc4f21161d7cc1f8134f2bd38b0ed0fdff1107b7 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 10 Aug 2023 16:30:36 +0200 Subject: [PATCH 061/114] create collection has param uri --- pallets/living-assets-ownership/src/lib.rs | 2 +- pallets/living-assets-ownership/src/tests.rs | 35 ++++++++++++++++---- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index f5b498c3..6603369f 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -68,7 +68,7 @@ pub mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO set proper weight - pub fn create_collection(origin: OriginFor) -> DispatchResult { + pub fn create_collection(origin: OriginFor, _base_uri: Vec) -> DispatchResult { let who = ensure_signed(origin)?; match Self::do_create_collection(who) { diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 824f3ba7..305adbdd 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -26,11 +26,20 @@ fn create_new_collection() { new_test_ext().execute_with(|| { assert_eq!(LivingAssetsModule::owner_of_collection(0), None); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); assert_eq!(LivingAssetsModule::owner_of_collection(0).unwrap(), ALICE); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); assert_eq!(LivingAssetsModule::owner_of_collection(1).unwrap(), ALICE); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); assert_eq!(LivingAssetsModule::owner_of_collection(2).unwrap(), ALICE); }); } @@ -41,13 +50,25 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { // Go past genesis block so events get deposited System::set_block_number(1); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); System::assert_last_event(Event::CollectionCreated { collection_id: 1, who: ALICE }.into()); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); System::assert_last_event(Event::CollectionCreated { collection_id: 2, who: ALICE }.into()); - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE))); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + "ciao".into() + )); System::assert_last_event(Event::CollectionCreated { collection_id: 3, who: ALICE }.into()); }); } From 2ddf57db27fa7182cc03a5cf32d8409dd6614c51 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:02:15 +0200 Subject: [PATCH 062/114] remove unused error --- pallets/living-assets-ownership/src/traits.rs | 2 -- precompile/living-assets/src/tests.rs | 13 ++++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index f958f8be..fa9b7f33 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -41,7 +41,6 @@ pub trait CollectionManager { /// - `UnknownError`: An unspecified error occurred. #[derive(Debug, PartialEq)] pub enum CollectionManagerError { - CollectionAlreadyExists, CollectionIdOverflow, UnknownError, } @@ -49,7 +48,6 @@ pub enum CollectionManagerError { impl AsRef<[u8]> for CollectionManagerError { fn as_ref(&self) -> &[u8] { match self { - CollectionManagerError::CollectionAlreadyExists => b"CollectionAlreadyExists", CollectionManagerError::CollectionIdOverflow => b"CollectionIdOverflow", CollectionManagerError::UnknownError => b"UnknownError", } diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index a9109207..46800121 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -34,20 +34,22 @@ fn check_log_selectors() { fn failing_create_collection_should_return_error() { impl_precompile_mock_simple!( Mock, - Err(CollectionManagerError::CollectionAlreadyExists), + Err(CollectionManagerError::UnknownError), Some(H160::zero()) ); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let mut handle = + create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); - assert_eq!(result.unwrap_err(), revert(CollectionManagerError::CollectionAlreadyExists)); + assert_eq!(result.unwrap_err(), revert(CollectionManagerError::UnknownError)); } #[test] fn create_collection_should_return_address() { impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let mut handle = + create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); assert_eq!( @@ -63,7 +65,8 @@ fn create_collection_should_return_address() { fn create_collection_should_generate_log() { impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(H160::zero())); - let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let mut handle = + create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); assert!(result.is_ok()); let logs = handle.logs; From 4ef9e9c79e185a4ea137e68477a99a8227f93b45 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:07:36 +0200 Subject: [PATCH 063/114] using sp-std --- pallets/living-assets-ownership/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index 10def798..73aaadac 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -22,6 +22,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } sp-arithmetic = { workspace = true } sp-core = { workspace = true } +sp-std = { workspace = true } [dev-dependencies] serde = { workspace = true } @@ -47,6 +48,7 @@ std = [ "frame-system/std", "sp-arithmetic/std", "sp-core/std", + "sp-std/std", ] try-runtime = [ "frame-system/try-runtime", From 746fed67eed09f928b9a631c01ed7a0185511d9a Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:07:57 +0200 Subject: [PATCH 064/114] using sp-std --- pallets/living-assets-ownership/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 8d8dd28a..4f0d614c 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -5,6 +5,7 @@ /// pub use pallet::*; use sp_core::H160; +use sp_std::vec::Vec; mod functions; pub mod traits; From b0738dde3c7220edb19a67bd550d0470a3880032 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:47:10 +0200 Subject: [PATCH 065/114] created CollectionBaseURI --- Cargo.lock | 1 + pallets/living-assets-ownership/src/lib.rs | 15 ++++++++++++++- pallets/living-assets-ownership/src/mock.rs | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c99b7a51..4ebe695c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7187,6 +7187,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-std", ] [[package]] diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 4f0d614c..b316168c 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -15,7 +15,10 @@ pub mod pallet { use crate::functions::convert_asset_id_to_owner; use super::*; - use frame_support::pallet_prelude::{OptionQuery, ValueQuery, *}; + use frame_support::{ + pallet_prelude::{OptionQuery, ValueQuery, *}, + BoundedVec, + }; use frame_system::pallet_prelude::*; use sp_core::{H160, U256}; @@ -31,6 +34,10 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + + /// The maximum length of a base URI + #[pallet::constant] + type BaseURILimit: Get; } /// Mapping from collection id to owner @@ -44,6 +51,12 @@ pub mod pallet { #[pallet::getter(fn collection_counter)] pub(super) type CollectionCounter = StorageValue<_, CollectionId, ValueQuery>; + /// Collection base URI + #[pallet::storage] + #[pallet::getter(fn collection_base_uri)] + pub(super) type CollectionBaseURI = + StorageMap<_, Blake2_128Concat, CollectionId, BoundedVec, OptionQuery>; + /// Pallet events #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index ca8fcaa8..3f009739 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -1,5 +1,5 @@ use crate as pallet_livingassets_ownership; -use frame_support::traits::{ConstU16, ConstU64}; +use frame_support::traits::{ConstU16, ConstU32, ConstU64}; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -46,6 +46,7 @@ impl frame_system::Config for Test { impl pallet_livingassets_ownership::Config for Test { type RuntimeEvent = RuntimeEvent; + type BaseURILimit = ConstU32<255>; } // Build genesis storage according to the mock runtime. From 053a1ea57a68ef5196b0007103711855fa9f05ce Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:47:30 +0200 Subject: [PATCH 066/114] created CollectionBaseURI --- pallets/living-assets-ownership/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index b316168c..a3ae6c05 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -25,6 +25,7 @@ pub mod pallet { /// Collection id type /// TODO: use 256 bits pub type CollectionId = u64; + type BaseURILimit: Get; #[pallet::pallet] pub struct Pallet(_); @@ -35,9 +36,9 @@ pub mod pallet { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// The maximum length of a base URI - #[pallet::constant] - type BaseURILimit: Get; + // /// The maximum length of a base URI + // #[pallet::constant] + // type BaseURILimit: Get; } /// Mapping from collection id to owner From 30076821fa60efce1091f02847c6c0599ee22071 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 10:49:24 +0200 Subject: [PATCH 067/114] BaseURILimit is defined --- pallets/living-assets-ownership/src/lib.rs | 7 ++++--- pallets/living-assets-ownership/src/mock.rs | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index a3ae6c05..df4a568d 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -23,9 +23,10 @@ pub mod pallet { use sp_core::{H160, U256}; /// Collection id type - /// TODO: use 256 bits pub type CollectionId = u64; - type BaseURILimit: Get; + + /// Base URI limit + type BaseURILimit = ConstU32<255>; #[pallet::pallet] pub struct Pallet(_); @@ -56,7 +57,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn collection_base_uri)] pub(super) type CollectionBaseURI = - StorageMap<_, Blake2_128Concat, CollectionId, BoundedVec, OptionQuery>; + StorageMap<_, Blake2_128Concat, CollectionId, BoundedVec, OptionQuery>; /// Pallet events #[pallet::event] diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index 3f009739..ca8fcaa8 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -1,5 +1,5 @@ use crate as pallet_livingassets_ownership; -use frame_support::traits::{ConstU16, ConstU32, ConstU64}; +use frame_support::traits::{ConstU16, ConstU64}; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -46,7 +46,6 @@ impl frame_system::Config for Test { impl pallet_livingassets_ownership::Config for Test { type RuntimeEvent = RuntimeEvent; - type BaseURILimit = ConstU32<255>; } // Build genesis storage according to the mock runtime. From d817778da3ef100ffd4c37e1ad001fa179ec5210 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 11:36:19 +0200 Subject: [PATCH 068/114] insering the base uri --- pallets/living-assets-ownership/src/functions.rs | 12 ++++++++++-- pallets/living-assets-ownership/src/lib.rs | 8 +++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index 9065d9cf..52e04e12 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -1,11 +1,14 @@ //! Contains helper and utility functions of the pallet use super::*; -use frame_support::sp_runtime::traits::One; +use frame_support::{sp_runtime::traits::One, BoundedVec}; use sp_core::{H160, U256}; impl Pallet { /// See [Self::create_collection] - pub fn do_create_collection(who: T::AccountId) -> Result> { + pub fn do_create_collection( + who: T::AccountId, + base_uri: Vec, + ) -> Result> { // Retrieve the current collection count to use as the new collection's ID let collection_id = Self::collection_counter(); @@ -13,6 +16,11 @@ impl Pallet { // collection's ID to the owner's account ID OwnerOfCollection::::insert(collection_id, &who); + let bounded_base_uri: BoundedVec<_, _> = + base_uri.try_into().map_err(|_| Error::::TooLong)?; + + CollectionBaseURI::::insert(collection_id, bounded_base_uri); + // Attempt to increment the collection counter by 1. If this operation // would result in an overflow, return early with an error let counter = diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index df4a568d..779cf303 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -73,6 +73,8 @@ pub mod pallet { pub enum Error { /// Collection id overflow CollectionIdOverflow, + /// value is too long + TooLong, } // Dispatchable functions allows users to interact with the pallet and invoke state changes. @@ -82,10 +84,10 @@ pub mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO set proper weight - pub fn create_collection(origin: OriginFor, _base_uri: Vec) -> DispatchResult { + pub fn create_collection(origin: OriginFor, base_uri: Vec) -> DispatchResult { let who = ensure_signed(origin)?; - match Self::do_create_collection(who) { + match Self::do_create_collection(who, base_uri) { Ok(_) => Ok(()), Err(err) => Err(err.into()), } @@ -100,7 +102,7 @@ pub mod pallet { fn create_collection( owner: T::AccountId, ) -> Result { - match Self::do_create_collection(owner) { + match Self::do_create_collection(owner, Vec::new()) { Ok(collection_id) => Ok(collection_id), Err(err) => match err { Error::CollectionIdOverflow => { From 95dac77547cdcb4e10c780fa198d6fd70d504531 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 11:39:51 +0200 Subject: [PATCH 069/114] insering the base uri --- pallets/living-assets-ownership/src/tests.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 305adbdd..f909277b 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -44,6 +44,18 @@ fn create_new_collection() { }); } +#[test] +fn create_new_collection_shoud_set_the_base_uri() { + let base_uri = "https://example.com/"; + new_test_ext().execute_with(|| { + assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE), base_uri.into())); + assert_eq!( + LivingAssetsModule::collection_base_uri(0).unwrap(), + base_uri.as_bytes().to_vec() + ); + }); +} + #[test] fn create_new_collections_should_emit_events_with_collection_id_consecutive() { new_test_ext().execute_with(|| { From d05b7b2e87deed1cd10e3a2fbe9083bd8ba3f630 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 11:46:03 +0200 Subject: [PATCH 070/114] base_uri is set in storage --- pallets/living-assets-ownership/src/tests.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index f909277b..52251793 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -45,14 +45,14 @@ fn create_new_collection() { } #[test] -fn create_new_collection_shoud_set_the_base_uri() { - let base_uri = "https://example.com/"; +fn should_set_base_uri_when_creating_new_collection() { + let base_uri: Vec = "https://example.com/".into(); new_test_ext().execute_with(|| { - assert_ok!(LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE), base_uri.into())); - assert_eq!( - LivingAssetsModule::collection_base_uri(0).unwrap(), - base_uri.as_bytes().to_vec() - ); + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + base_uri.clone() + )); + assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); }); } From f0dec746ab07454b7ad67221bdcdf462d89c6a90 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 12:55:01 +0200 Subject: [PATCH 071/114] create_collection trait has base_uri as param --- pallets/living-assets-ownership/src/lib.rs | 3 +- pallets/living-assets-ownership/src/tests.rs | 64 ++++++++++++++++--- pallets/living-assets-ownership/src/traits.rs | 5 +- 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 779cf303..2575b15e 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -101,8 +101,9 @@ pub mod pallet { fn create_collection( owner: T::AccountId, + base_uri: Vec, ) -> Result { - match Self::do_create_collection(owner, Vec::new()) { + match Self::do_create_collection(owner, base_uri) { Ok(collection_id) => Ok(collection_id), Err(err) => match err { Error::CollectionIdOverflow => { diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 52251793..a3bb6d0d 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -88,7 +88,10 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { #[test] fn living_assets_ownership_trait_create_new_collection() { new_test_ext().execute_with(|| { - let result = >::create_collection(ALICE); + let result = >::create_collection( + ALICE, + Vec::new(), + ); assert_ok!(result); assert_eq!(LivingAssetsModule::owner_of_collection(0).unwrap(), ALICE); }); @@ -114,7 +117,10 @@ fn living_assets_ownership_trait_create_new_collection_should_emit_an_event() { // Go past genesis block so events get deposited System::set_block_number(1); - assert_ok!(>::create_collection(ALICE)); + assert_ok!(>::create_collection( + ALICE, + Vec::new() + )); System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); }); } @@ -123,32 +129,68 @@ fn living_assets_ownership_trait_create_new_collection_should_emit_an_event() { fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { new_test_ext().execute_with(|| { assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 0 ); assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 1 ); assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 2 ); assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 3 ); assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 4 ); assert_eq!( - >::create_collection(ALICE).unwrap(), + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), 5 ); }); } +#[test] +fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { + let base_uri: Vec = "https://example.com/".into(); + new_test_ext().execute_with(|| { + assert_ok!(>::create_collection( + ALICE, + base_uri.clone() + )); + assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); + }); +} + #[test] fn erc721_owner_of_asset_of_unexistent_collection() { new_test_ext().execute_with(|| { @@ -161,7 +203,11 @@ fn erc721_owner_of_asset_of_unexistent_collection() { fn erc721_owner_of_asset_of_collection() { new_test_ext().execute_with(|| { let collection_id = - >::create_collection(ALICE).unwrap(); + >::create_collection( + ALICE, + Vec::new(), + ) + .unwrap(); assert_eq!( ::owner_of(collection_id, 2.into()).unwrap(), H160::from_low_u64_be(0x0000000000000002) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index fa9b7f33..0c22fb87 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -31,7 +31,10 @@ pub trait CollectionManager { /// # Returns /// /// A result containing the `collection_id` of the newly created collection or an error. - fn create_collection(owner: AccountId) -> Result; + fn create_collection( + owner: AccountId, + base_uri: Vec, + ) -> Result; } /// Errors that can occur when managing collections. From 2a074d09741d8c1e1372cbfda9f28086eb965047 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 15:02:14 +0200 Subject: [PATCH 072/114] living_assets_ownership pallet tests are green --- .../living-assets-ownership/src/functions.rs | 4 - pallets/living-assets-ownership/src/lib.rs | 23 +-- pallets/living-assets-ownership/src/tests.rs | 177 +++--------------- pallets/living-assets-ownership/src/traits.rs | 133 ++++++++++++- 4 files changed, 159 insertions(+), 178 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index 52e04e12..c0e94018 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -12,10 +12,6 @@ impl Pallet { // Retrieve the current collection count to use as the new collection's ID let collection_id = Self::collection_counter(); - // Insert a new entry into the OwnerOfCollection map, mapping the new - // collection's ID to the owner's account ID - OwnerOfCollection::::insert(collection_id, &who); - let bounded_base_uri: BoundedVec<_, _> = base_uri.try_into().map_err(|_| Error::::TooLong)?; diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 2575b15e..749de46c 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -25,9 +25,12 @@ pub mod pallet { /// Collection id type pub type CollectionId = u64; - /// Base URI limit + /// Base URI limit type type BaseURILimit = ConstU32<255>; + /// Base URI type + pub type BaseURI = BoundedVec; + #[pallet::pallet] pub struct Pallet(_); @@ -36,18 +39,8 @@ pub mod pallet { pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - - // /// The maximum length of a base URI - // #[pallet::constant] - // type BaseURILimit: Get; } - /// Mapping from collection id to owner - #[pallet::storage] - #[pallet::getter(fn owner_of_collection)] - pub(super) type OwnerOfCollection = - StorageMap<_, Blake2_128Concat, CollectionId, T::AccountId, OptionQuery>; - /// Collection counter #[pallet::storage] #[pallet::getter(fn collection_counter)] @@ -57,7 +50,7 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn collection_base_uri)] pub(super) type CollectionBaseURI = - StorageMap<_, Blake2_128Concat, CollectionId, BoundedVec, OptionQuery>; + StorageMap<_, Blake2_128Concat, CollectionId, BaseURI, OptionQuery>; /// Pallet events #[pallet::event] @@ -95,8 +88,8 @@ pub mod pallet { } impl traits::CollectionManager for Pallet { - fn owner_of_collection(collection_id: CollectionId) -> Option { - OwnerOfCollection::::get(collection_id) + fn base_uri(collection_id: CollectionId) -> Option { + CollectionBaseURI::::get(collection_id) } fn create_collection( @@ -120,7 +113,7 @@ pub mod pallet { collection_id: CollectionId, asset_id: U256, ) -> Result { - match OwnerOfCollection::::get(collection_id) { + match CollectionBaseURI::::get(collection_id) { Some(_) => Ok(convert_asset_id_to_owner(asset_id)), None => Err(traits::Erc721Error::UnexistentCollection), } diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index a3bb6d0d..9f863217 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,12 +1,10 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, is_collection_address, - mock::*, - traits::{self, CollectionManager, Erc721}, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, BaseURI, CollectionError, Event, }; -use frame_support::{assert_err, assert_ok}; +use frame_support::assert_ok; use sp_core::H160; type AccountId = ::AccountId; @@ -14,33 +12,32 @@ type AccountId = ::AccountId; const ALICE: AccountId = 0x1234; #[test] -fn owner_of_unexistent_collection_is_none() { +fn base_uri_unexistent_collection_is_none() { new_test_ext().execute_with(|| { - assert_eq!(LivingAssetsModule::owner_of_collection(0), None); - assert_eq!(LivingAssetsModule::owner_of_collection(1), None); + assert_eq!(LivingAssetsModule::collection_base_uri(0), None); + assert_eq!(LivingAssetsModule::collection_base_uri(1), None); }); } #[test] -fn create_new_collection() { +fn create_new_collection_should_create_sequential_collections() { new_test_ext().execute_with(|| { - assert_eq!(LivingAssetsModule::owner_of_collection(0), None); - - assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), - "ciao".into() - )); - assert_eq!(LivingAssetsModule::owner_of_collection(0).unwrap(), ALICE); - assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), - "ciao".into() - )); - assert_eq!(LivingAssetsModule::owner_of_collection(1).unwrap(), ALICE); - assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), - "ciao".into() - )); - assert_eq!(LivingAssetsModule::owner_of_collection(2).unwrap(), ALICE); + // Check initial condition + assert_eq!(LivingAssetsModule::collection_base_uri(0), None); + + // Iterate through the collections to be created + for i in 0..3 { + let base_uri: Vec = format!("https://example.com/{}", i).into(); + + // Create the collection + assert_ok!(LivingAssetsModule::create_collection( + RuntimeOrigin::signed(ALICE), + base_uri.clone() + )); + + // Assert that the collection was created with the expected URI + assert_eq!(LivingAssetsModule::collection_base_uri(i).unwrap(), base_uri); + } }); } @@ -85,136 +82,6 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { }); } -#[test] -fn living_assets_ownership_trait_create_new_collection() { - new_test_ext().execute_with(|| { - let result = >::create_collection( - ALICE, - Vec::new(), - ); - assert_ok!(result); - assert_eq!(LivingAssetsModule::owner_of_collection(0).unwrap(), ALICE); - }); -} - -#[test] -fn living_assets_ownership_trait_owner_of_unexistent_collection_is_none() { - new_test_ext().execute_with(|| { - assert_eq!( - >::owner_of_collection(0), - None - ); - assert_eq!( - >::owner_of_collection(1), - None - ); - }); -} - -#[test] -fn living_assets_ownership_trait_create_new_collection_should_emit_an_event() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - - assert_ok!(>::create_collection( - ALICE, - Vec::new() - )); - System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); - }); -} - -#[test] -fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { - new_test_ext().execute_with(|| { - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 0 - ); - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 1 - ); - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 2 - ); - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 3 - ); - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 4 - ); - assert_eq!( - >::create_collection( - ALICE, - Vec::new() - ) - .unwrap(), - 5 - ); - }); -} - -#[test] -fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { - let base_uri: Vec = "https://example.com/".into(); - new_test_ext().execute_with(|| { - assert_ok!(>::create_collection( - ALICE, - base_uri.clone() - )); - assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); - }); -} - -#[test] -fn erc721_owner_of_asset_of_unexistent_collection() { - new_test_ext().execute_with(|| { - let result = ::owner_of(0, 2.into()); - assert_err!(result, traits::Erc721Error::UnexistentCollection); - }); -} - -#[test] -fn erc721_owner_of_asset_of_collection() { - new_test_ext().execute_with(|| { - let collection_id = - >::create_collection( - ALICE, - Vec::new(), - ) - .unwrap(); - assert_eq!( - ::owner_of(collection_id, 2.into()).unwrap(), - H160::from_low_u64_be(0x0000000000000002) - ); - }); -} - #[test] fn test_collection_id_to_address() { let collection_id = 5; diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 0c22fb87..ebdde580 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,4 +1,4 @@ -use crate::CollectionId; +use crate::{BaseURI, CollectionId}; use sp_core::{H160, U256}; /// The `CollectionManager` trait provides an interface for managing collections in a decentralized system. @@ -11,7 +11,7 @@ use sp_core::{H160, U256}; /// - `owner_of_collection`: Retrieve the owner of a specified collection. /// - `create_collection`: Create a new collection and assign it to an owner. pub trait CollectionManager { - /// Retrieves the owner of the specified collection. + /// Retrieves the base uri of the specified collection. /// /// # Arguments /// @@ -19,8 +19,8 @@ pub trait CollectionManager { /// /// # Returns /// - /// The account ID of the collection's owner or `None` if the collection doesn't exist. - fn owner_of_collection(collection_id: CollectionId) -> Option; + /// The base URI associated with the specified collection or `None` if the collection doesn't exist. + fn base_uri(collection_id: CollectionId) -> Option; /// Creates a new collection and assigns it to the specified owner. /// @@ -93,3 +93,128 @@ impl AsRef<[u8]> for Erc721Error { } } } + +#[cfg(test)] +mod tests { + use frame_support::{assert_err, assert_ok}; + + use super::*; + use crate::{mock::*, Event}; + + type AccountId = ::AccountId; + const ALICE: AccountId = 0x1234; + + #[test] + fn base_uri_of_unexistent_collection_is_none() { + new_test_ext().execute_with(|| { + assert_eq!(>::base_uri(0), None); + assert_eq!(>::base_uri(1), None); + }); + } + + #[test] + fn create_new_collection_should_emit_an_event() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + + assert_ok!(>::create_collection( + ALICE, + Vec::new() + )); + System::assert_last_event( + Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), + ); + }); + } + + #[test] + fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { + new_test_ext().execute_with(|| { + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 0 + ); + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 1 + ); + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 2 + ); + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 3 + ); + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 4 + ); + assert_eq!( + >::create_collection( + ALICE, + Vec::new() + ) + .unwrap(), + 5 + ); + }); + } + + #[test] + fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { + let base_uri: Vec = "https://example.com/".into(); + new_test_ext().execute_with(|| { + assert_ok!(>::create_collection( + ALICE, + base_uri.clone() + )); + assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); + }); + } + + #[test] + fn owner_of_asset_of_unexistent_collection_should_error() { + new_test_ext().execute_with(|| { + let result = ::owner_of(0, 2.into()); + assert_err!(result, Erc721Error::UnexistentCollection); + }); + } + + #[test] + fn erc721_owner_of_asset_of_collection() { + new_test_ext().execute_with(|| { + let collection_id = + >::create_collection( + ALICE, + Vec::new(), + ) + .unwrap(); + assert_eq!( + ::owner_of(collection_id, 2.into()).unwrap(), + H160::from_low_u64_be(0x0000000000000002) + ); + }); + } +} From ffa3835551fe34d81b2c342a6613b5a6f41a1ccc Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 17:33:11 +0200 Subject: [PATCH 073/114] tests passing --- .../contracts/LivingAssetsOwnership.sol | 2 +- precompile/living-assets/src/lib.rs | 12 +++++- precompile/living-assets/src/tests.rs | 41 +++++++++++-------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/precompile/living-assets/contracts/LivingAssetsOwnership.sol b/precompile/living-assets/contracts/LivingAssetsOwnership.sol index 5b87e687..a702738a 100644 --- a/precompile/living-assets/contracts/LivingAssetsOwnership.sol +++ b/precompile/living-assets/contracts/LivingAssetsOwnership.sol @@ -13,5 +13,5 @@ interface LivingAssets { /// @notice Creates a new collection /// @dev Call this function to create a new collection /// @return address of the ERC721 collection - function createCollection(string memory tokenURI) external returns (address); + function createCollection(string memory baseURI) external returns (address); } diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index 5e90f505..509be4ea 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -7,7 +7,7 @@ use pallet_living_assets_ownership::{ }; use parity_scale_codec::Encode; use precompile_utils::{ - keccak256, revert, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, LogExt, + keccak256, revert, succeed, Address, Bytes, EvmDataWriter, EvmResult, FunctionModifier, LogExt, LogsBuilder, PrecompileHandleExt, }; use sp_runtime::SaturatedConversion; @@ -49,10 +49,18 @@ where match selector { Action::CreateCollection => { + let mut input = handle.read_input()?; + input.expect_arguments(1)?; + + let base_uri: Vec = match input.read::() { + Ok(bytes) => bytes.into(), + Err(e) => return Err(e), + }; + let caller = handle.context().caller; let owner = AddressMapping::into_account_id(caller); - match LivingAssets::create_collection(owner) { + match LivingAssets::create_collection(owner, base_uri) { Ok(collection_id) => { let collection_address = collection_id_to_address( collection_id.saturated_into::(), diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 46800121..e27bb408 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -4,7 +4,7 @@ #![allow(clippy::redundant_closure_call)] use super::*; -use pallet_living_assets_ownership::traits::CollectionManagerError; +use pallet_living_assets_ownership::{traits::CollectionManagerError, BaseURI}; use precompile_utils::{ revert, succeed, testing::{create_mock_handle, create_mock_handle_from_input}, @@ -15,7 +15,7 @@ use sp_std::vec::Vec; type AccountId = H160; type AddressMapping = pallet_evm::IdentityAddressMapping; -const CREATE_COLLECTION_WITH_URI: &str = "0x059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; +const CREATE_COLLECTION_WITH_URI: &str = "059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; #[test] fn check_selectors() { @@ -35,7 +35,7 @@ fn failing_create_collection_should_return_error() { impl_precompile_mock_simple!( Mock, Err(CollectionManagerError::UnknownError), - Some(H160::zero()) + Some(BaseURI::new()) ); let mut handle = @@ -46,7 +46,7 @@ fn failing_create_collection_should_return_error() { #[test] fn create_collection_should_return_address() { - impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); + impl_precompile_mock_simple!(Mock, Ok(5), Some(BaseURI::new())); let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); @@ -63,7 +63,7 @@ fn create_collection_should_return_address() { #[test] fn create_collection_should_generate_log() { - impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(H160::zero())); + impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(BaseURI::new())); let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); @@ -83,7 +83,7 @@ fn create_collection_should_generate_log() { #[test] fn create_collection_on_mock_with_nonzero_value_fails() { - impl_precompile_mock_simple!(Mock, Ok(5), Some(H160::zero())); + impl_precompile_mock_simple!(Mock, Ok(5), Some(BaseURI::new())); let mut handle = create_mock_handle(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap(), 0, 1, H160::zero()); let result = Mock::execute(&mut handle); @@ -95,11 +95,15 @@ fn create_collection_on_mock_with_nonzero_value_fails() { fn create_collection_assign_collection_to_caller() { impl_precompile_mock!( Mock, // name of the defined precompile - |owner| { + |owner, base_uri| { assert_eq!(owner, H160::from_low_u64_be(0x1234)); + assert_eq!( + base_uri, + vec![105, 112, 102, 115, 58, 47, 47, 99, 97, 114, 98, 111, 110, 97, 114, 97] + ); Ok(0) }, // Closure for create_collection result - |_| { Some(H160::zero()) } // Closure for owner_of_collection result + |_| { Some(BaseURI::new()) } // Closure for owner_of_collection result ); let mut handle = create_mock_handle( @@ -114,7 +118,7 @@ fn create_collection_assign_collection_to_caller() { #[test] fn call_unexistent_selector_should_fail() { - impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::from_low_u64_be(0x1234))); + impl_precompile_mock_simple!(Mock, Ok(0), Some(BaseURI::new())); let unexistent_selector = hex::decode("fb24ae530000000000000000000000000000000000000000000000000000000000000000") @@ -140,11 +144,11 @@ mod helpers { /// # Example /// /// ``` - /// impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::zero())); + /// impl_precompile_mock_simple!(Mock, Ok(0), Some(BaseURI::new()); /// ``` #[macro_export] macro_rules! impl_precompile_mock { - ($name:ident, $create_collection_result:expr, $owner_of_collection_result:expr) => { + ($name:ident, $create_collection_result:expr, $base_uri_result:expr) => { struct CollectionManagerMock; impl pallet_living_assets_ownership::traits::CollectionManager @@ -152,12 +156,13 @@ mod helpers { { fn create_collection( owner: AccountId, + base_uri: Vec, ) -> Result { - ($create_collection_result)(owner) + ($create_collection_result)(owner, base_uri) } - fn owner_of_collection(collection_id: CollectionId) -> Option { - ($owner_of_collection_result)(collection_id) + fn base_uri(collection_id: CollectionId) -> Option { + ($base_uri_result)(collection_id) } } @@ -180,15 +185,15 @@ mod helpers { /// # Example /// /// ``` - /// impl_precompile_mock_simple!(Mock, Ok(0), Some(H160::zero())); + /// impl_precompile_mock_simple!(Mock, Ok(0), Some(BaseURI::new()); /// ``` #[macro_export] macro_rules! impl_precompile_mock_simple { - ($name:ident, $create_collection_result:expr, $owner_of_collection_result:expr) => { + ($name:ident, $create_collection_result:expr, $base_uri_result:expr) => { impl_precompile_mock!( $name, - |_owner| { $create_collection_result }, - |_collection_id| { $owner_of_collection_result } + |_owner, _base_uri| { $create_collection_result }, + |_collection_id| { $base_uri_result } ); }; } From c15597196b3c0720c99ad19f13157cc29c5ab506 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 17:54:55 +0200 Subject: [PATCH 074/114] test on base_uri too long --- pallets/living-assets-ownership/src/lib.rs | 2 +- pallets/living-assets-ownership/src/tests.rs | 23 +++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 749de46c..1eca9ef2 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -26,7 +26,7 @@ pub mod pallet { pub type CollectionId = u64; /// Base URI limit type - type BaseURILimit = ConstU32<255>; + pub type BaseURILimit = ConstU32<255>; /// Base URI type pub type BaseURI = BoundedVec; diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 9f863217..a48c139c 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,11 +1,12 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, BaseURI, - CollectionError, Event, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, + BaseURILimit, CollectionError, Error, Event, }; use frame_support::assert_ok; -use sp_core::H160; +use sp_core::{Get, H160}; +use sp_runtime::{DispatchError, ModuleError}; type AccountId = ::AccountId; @@ -82,6 +83,22 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { }); } +#[test] +fn create_collection_with_base_uri_greater_than_limit() { + new_test_ext().execute_with(|| { + let base_uri: Vec = vec![0; 255 + 1]; + assert_eq!( + LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE), base_uri) + .unwrap_err(), + DispatchError::Module(ModuleError { + index: 1, + error: [1, 0, 0, 0], + message: Some("TooLong".into()), + }) + ); + }); +} + #[test] fn test_collection_id_to_address() { let collection_id = 5; From cb2c18d0de0e05bfaf035407ca637dcb86e0eb72 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 17:57:47 +0200 Subject: [PATCH 075/114] removing use --- pallets/living-assets-ownership/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index a48c139c..0146d990 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -2,10 +2,10 @@ use core::str::FromStr; use crate::{ address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, - BaseURILimit, CollectionError, Error, Event, + CollectionError, Event, }; use frame_support::assert_ok; -use sp_core::{Get, H160}; +use sp_core::H160; use sp_runtime::{DispatchError, ModuleError}; type AccountId = ::AccountId; @@ -86,7 +86,7 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { #[test] fn create_collection_with_base_uri_greater_than_limit() { new_test_ext().execute_with(|| { - let base_uri: Vec = vec![0; 255 + 1]; + let base_uri: Vec = vec![0; 255 + 1]; // TODO: use BaseURILimit::get() + 1 assert_eq!( LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE), base_uri) .unwrap_err(), From 4998cce05fba5327d9c95ffcac695b8b9e58c375 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Fri, 11 Aug 2023 18:16:31 +0200 Subject: [PATCH 076/114] use Vec --- pallets/living-assets-ownership/src/traits.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index ebdde580..6365ea45 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,5 +1,6 @@ use crate::{BaseURI, CollectionId}; use sp_core::{H160, U256}; +use sp_std::vec::Vec; /// The `CollectionManager` trait provides an interface for managing collections in a decentralized system. /// From 51342a0bcb16f4bbdf0ec3ce293960325e0223f4 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 09:20:57 +0200 Subject: [PATCH 077/114] testing minimum arguments --- precompile/erc721/src/tests.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index b9a7b1e2..4aef1f13 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -59,7 +59,26 @@ fn invalid_contract_address_should_error() { handle.code_address = H160::zero(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("tried to parse selector out of bounds"),); + assert_eq!(result.unwrap_err(), revert("tried to parse selector out of bounds")); +} + +#[test] +fn token_owners_should_have_at_least_token_id_as_argument() { + impl_precompile_mock_simple!(Mock, Ok(H160::zero())); + + let owner_of_with_2_arguments: Vec = + hex::decode("6352211e00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000004") + .unwrap(); + let mut handle = create_mock_handle_from_input(owner_of_with_2_arguments); + handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_ok()); + + let owner_of_with_0_arguments: Vec = hex::decode("6352211e").unwrap(); + let mut handle = create_mock_handle_from_input(owner_of_with_0_arguments); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("input doesn't match expected length")); } mod helpers { From c39d92f1e47c9911e1423d814d1179015ec5f3ae Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:08:53 +0200 Subject: [PATCH 078/114] create_collection has a BondedVec tokenURI --- .../living-assets-ownership/src/functions.rs | 2 +- pallets/living-assets-ownership/src/lib.rs | 5 +-- pallets/living-assets-ownership/src/tests.rs | 39 +++++++------------ pallets/living-assets-ownership/src/traits.rs | 28 +++++++------ 4 files changed, 34 insertions(+), 40 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index c0e94018..380a7076 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -7,7 +7,7 @@ impl Pallet { /// See [Self::create_collection] pub fn do_create_collection( who: T::AccountId, - base_uri: Vec, + base_uri: BaseURI, ) -> Result> { // Retrieve the current collection count to use as the new collection's ID let collection_id = Self::collection_counter(); diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 1eca9ef2..487ec08a 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -5,7 +5,6 @@ /// pub use pallet::*; use sp_core::H160; -use sp_std::vec::Vec; mod functions; pub mod traits; @@ -77,7 +76,7 @@ pub mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO set proper weight - pub fn create_collection(origin: OriginFor, base_uri: Vec) -> DispatchResult { + pub fn create_collection(origin: OriginFor, base_uri: BaseURI) -> DispatchResult { let who = ensure_signed(origin)?; match Self::do_create_collection(who, base_uri) { @@ -94,7 +93,7 @@ pub mod pallet { fn create_collection( owner: T::AccountId, - base_uri: Vec, + base_uri: BaseURI, ) -> Result { match Self::do_create_collection(owner, base_uri) { Ok(collection_id) => Ok(collection_id), diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 0146d990..33f3f88f 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,12 +1,11 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, BaseURI, CollectionError, Event, }; use frame_support::assert_ok; use sp_core::H160; -use sp_runtime::{DispatchError, ModuleError}; type AccountId = ::AccountId; @@ -26,10 +25,10 @@ fn create_new_collection_should_create_sequential_collections() { // Check initial condition assert_eq!(LivingAssetsModule::collection_base_uri(0), None); + let base_uri = base_uri_from_string("https://example.com/"); + // Iterate through the collections to be created for i in 0..3 { - let base_uri: Vec = format!("https://example.com/{}", i).into(); - // Create the collection assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), @@ -44,7 +43,7 @@ fn create_new_collection_should_create_sequential_collections() { #[test] fn should_set_base_uri_when_creating_new_collection() { - let base_uri: Vec = "https://example.com/".into(); + let base_uri = base_uri_from_string("https://example.com/"); new_test_ext().execute_with(|| { assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), @@ -62,43 +61,27 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - "ciao".into() + base_uri_from_string("ciao") )); System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - "ciao".into() + base_uri_from_string("ciao") )); System::assert_last_event(Event::CollectionCreated { collection_id: 1, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - "ciao".into() + base_uri_from_string("ciao") )); System::assert_last_event(Event::CollectionCreated { collection_id: 2, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - "ciao".into() + base_uri_from_string("ciao") )); System::assert_last_event(Event::CollectionCreated { collection_id: 3, who: ALICE }.into()); }); } -#[test] -fn create_collection_with_base_uri_greater_than_limit() { - new_test_ext().execute_with(|| { - let base_uri: Vec = vec![0; 255 + 1]; // TODO: use BaseURILimit::get() + 1 - assert_eq!( - LivingAssetsModule::create_collection(RuntimeOrigin::signed(ALICE), base_uri) - .unwrap_err(), - DispatchError::Module(ModuleError { - index: 1, - error: [1, 0, 0, 0], - message: Some("TooLong".into()), - }) - ); - }); -} - #[test] fn test_collection_id_to_address() { let collection_id = 5; @@ -134,3 +117,9 @@ fn test_is_collection_address_invalid() { assert!(!is_collection_address(invalid_address)); } + +fn base_uri_from_string(url: &str) -> BaseURI { + let mut base_uri = BaseURI::new(); + base_uri.try_append(&mut url.to_string().into_bytes()).unwrap(); + base_uri +} diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 6365ea45..2390dae6 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,6 +1,5 @@ use crate::{BaseURI, CollectionId}; use sp_core::{H160, U256}; -use sp_std::vec::Vec; /// The `CollectionManager` trait provides an interface for managing collections in a decentralized system. /// @@ -34,7 +33,7 @@ pub trait CollectionManager { /// A result containing the `collection_id` of the newly created collection or an error. fn create_collection( owner: AccountId, - base_uri: Vec, + base_uri: BaseURI, ) -> Result; } @@ -121,7 +120,7 @@ mod tests { assert_ok!(>::create_collection( ALICE, - Vec::new() + BaseURI::default(), )); System::assert_last_event( Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), @@ -135,7 +134,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 0 @@ -143,7 +142,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 1 @@ -151,7 +150,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 2 @@ -159,7 +158,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 3 @@ -167,7 +166,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 4 @@ -175,7 +174,7 @@ mod tests { assert_eq!( >::create_collection( ALICE, - Vec::new() + BaseURI::default() ) .unwrap(), 5 @@ -185,7 +184,8 @@ mod tests { #[test] fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { - let base_uri: Vec = "https://example.com/".into(); + let base_uri = base_uri_from_string("https://example.com/"); + new_test_ext().execute_with(|| { assert_ok!(>::create_collection( ALICE, @@ -209,7 +209,7 @@ mod tests { let collection_id = >::create_collection( ALICE, - Vec::new(), + BaseURI::default(), ) .unwrap(); assert_eq!( @@ -218,4 +218,10 @@ mod tests { ); }); } + + fn base_uri_from_string(url: &str) -> BaseURI { + let mut base_uri = BaseURI::new(); + base_uri.try_append(&mut url.to_string().into_bytes()).unwrap(); + base_uri + } } From a9f705349392f27c7aa93f91c8ac1ba9a7951c75 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:14:07 +0200 Subject: [PATCH 079/114] removed unued error --- pallets/living-assets-ownership/src/functions.rs | 7 ++----- pallets/living-assets-ownership/src/lib.rs | 2 -- precompile/living-assets/src/lib.rs | 9 ++++++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index 380a7076..e6145f57 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -1,6 +1,6 @@ //! Contains helper and utility functions of the pallet use super::*; -use frame_support::{sp_runtime::traits::One, BoundedVec}; +use frame_support::sp_runtime::traits::One; use sp_core::{H160, U256}; impl Pallet { @@ -12,10 +12,7 @@ impl Pallet { // Retrieve the current collection count to use as the new collection's ID let collection_id = Self::collection_counter(); - let bounded_base_uri: BoundedVec<_, _> = - base_uri.try_into().map_err(|_| Error::::TooLong)?; - - CollectionBaseURI::::insert(collection_id, bounded_base_uri); + CollectionBaseURI::::insert(collection_id, base_uri); // Attempt to increment the collection counter by 1. If this operation // would result in an overflow, return early with an error diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 487ec08a..c756bce0 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -65,8 +65,6 @@ pub mod pallet { pub enum Error { /// Collection id overflow CollectionIdOverflow, - /// value is too long - TooLong, } // Dispatchable functions allows users to interact with the pallet and invoke state changes. diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index 509be4ea..645d6024 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{ - collection_id_to_address, traits::CollectionManager, CollectionId, + collection_id_to_address, traits::CollectionManager, CollectionId, BaseURI, }; use parity_scale_codec::Encode; use precompile_utils::{ @@ -52,15 +52,18 @@ where let mut input = handle.read_input()?; input.expect_arguments(1)?; - let base_uri: Vec = match input.read::() { + let mut base_uri: Vec = match input.read::() { Ok(bytes) => bytes.into(), Err(e) => return Err(e), }; + let mut bb = BaseURI::new(); + bb.try_append(&mut base_uri); + let caller = handle.context().caller; let owner = AddressMapping::into_account_id(caller); - match LivingAssets::create_collection(owner, base_uri) { + match LivingAssets::create_collection(owner, bb) { Ok(collection_id) => { let collection_address = collection_id_to_address( collection_id.saturated_into::(), From 724d57164751cac1be04c7478de5f3b58068e3ba Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:18:21 +0200 Subject: [PATCH 080/114] fix compilation --- precompile/living-assets/src/lib.rs | 7 +++++-- precompile/living-assets/src/tests.rs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index 645d6024..4fd6a33d 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{ - collection_id_to_address, traits::CollectionManager, CollectionId, BaseURI, + collection_id_to_address, traits::CollectionManager, BaseURI, CollectionId, }; use parity_scale_codec::Encode; use precompile_utils::{ @@ -58,7 +58,10 @@ where }; let mut bb = BaseURI::new(); - bb.try_append(&mut base_uri); + match bb.try_append(&mut base_uri) { + Ok(()) => (), + Err(_) => return Err(revert("base_uri too long")), + } let caller = handle.context().caller; let owner = AddressMapping::into_account_id(caller); diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index e27bb408..aa68a48b 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -156,7 +156,7 @@ mod helpers { { fn create_collection( owner: AccountId, - base_uri: Vec, + base_uri: BaseURI, ) -> Result { ($create_collection_result)(owner, base_uri) } From 1d3b5bcc7699e871a8cb90cadd3836953d74626f Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:30:31 +0200 Subject: [PATCH 081/114] refactoring --- pallets/living-assets-ownership/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index c756bce0..ecee594d 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -25,7 +25,8 @@ pub mod pallet { pub type CollectionId = u64; /// Base URI limit type - pub type BaseURILimit = ConstU32<255>; + const BASE_URI_LIMIT_U32: u32 = u8::MAX as u32; + pub type BaseURILimit = ConstU32; /// Base URI type pub type BaseURI = BoundedVec; From 95a3d077636a3be13b0ad90d65ea74e632aa497b Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:35:47 +0200 Subject: [PATCH 082/114] refactoring --- pallets/living-assets-ownership/src/tests.rs | 19 +++++++------------ pallets/living-assets-ownership/src/traits.rs | 8 +------- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 33f3f88f..16e3b0d2 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -25,7 +25,7 @@ fn create_new_collection_should_create_sequential_collections() { // Check initial condition assert_eq!(LivingAssetsModule::collection_base_uri(0), None); - let base_uri = base_uri_from_string("https://example.com/"); + let base_uri = BaseURI::try_from("https://example.com/".as_bytes().to_vec()).unwrap(); // Iterate through the collections to be created for i in 0..3 { @@ -43,7 +43,8 @@ fn create_new_collection_should_create_sequential_collections() { #[test] fn should_set_base_uri_when_creating_new_collection() { - let base_uri = base_uri_from_string("https://example.com/"); + let base_uri = BaseURI::try_from("https://example.com/".as_bytes().to_vec()).unwrap(); + new_test_ext().execute_with(|| { assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), @@ -61,22 +62,22 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - base_uri_from_string("ciao") + BaseURI::default() )); System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - base_uri_from_string("ciao") + BaseURI::default() )); System::assert_last_event(Event::CollectionCreated { collection_id: 1, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - base_uri_from_string("ciao") + BaseURI::default() )); System::assert_last_event(Event::CollectionCreated { collection_id: 2, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( RuntimeOrigin::signed(ALICE), - base_uri_from_string("ciao") + BaseURI::default() )); System::assert_last_event(Event::CollectionCreated { collection_id: 3, who: ALICE }.into()); }); @@ -117,9 +118,3 @@ fn test_is_collection_address_invalid() { assert!(!is_collection_address(invalid_address)); } - -fn base_uri_from_string(url: &str) -> BaseURI { - let mut base_uri = BaseURI::new(); - base_uri.try_append(&mut url.to_string().into_bytes()).unwrap(); - base_uri -} diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 2390dae6..7c6e759e 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -184,7 +184,7 @@ mod tests { #[test] fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { - let base_uri = base_uri_from_string("https://example.com/"); + let base_uri = BaseURI::try_from("https://example.com/".as_bytes().to_vec()).unwrap(); new_test_ext().execute_with(|| { assert_ok!(>::create_collection( @@ -218,10 +218,4 @@ mod tests { ); }); } - - fn base_uri_from_string(url: &str) -> BaseURI { - let mut base_uri = BaseURI::new(); - base_uri.try_append(&mut url.to_string().into_bytes()).unwrap(); - base_uri - } } From e69a2b0919522a8f19bf99168cce854a39f9858e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 11:55:55 +0200 Subject: [PATCH 083/114] removed legacy doc --- pallets/living-assets-ownership/src/traits.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 7c6e759e..f2a677ee 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -39,7 +39,6 @@ pub trait CollectionManager { /// Errors that can occur when managing collections. /// -/// - `CollectionAlreadyExists`: A collection with the same ID already exists. /// - `CollectionIdOverflow`: The ID for the new collection would overflow. /// - `UnknownError`: An unspecified error occurred. #[derive(Debug, PartialEq)] From e8fa0af26d2426a8652f95e824ee7fd965ed028c Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 12:17:36 +0200 Subject: [PATCH 084/114] BaseURILimit type is not public --- pallets/living-assets-ownership/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index ecee594d..729ab73d 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -24,9 +24,10 @@ pub mod pallet { /// Collection id type pub type CollectionId = u64; - /// Base URI limit type + /// Base URI limit const BASE_URI_LIMIT_U32: u32 = u8::MAX as u32; - pub type BaseURILimit = ConstU32; + /// Base URI limit type + type BaseURILimit = ConstU32; /// Base URI type pub type BaseURI = BoundedVec; From 4b87845da68e63f0802664c638478fed6f502cc5 Mon Sep 17 00:00:00 2001 From: magecnion Date: Wed, 16 Aug 2023 13:12:03 +0200 Subject: [PATCH 085/114] add transfer_from dummy tests and func definitions --- pallets/living-assets-ownership/src/traits.rs | 18 +++- precompile/erc721/src/lib.rs | 8 +- precompile/erc721/src/tests.rs | 82 ++++++++++++++++--- 3 files changed, 94 insertions(+), 14 deletions(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index f958f8be..4daf094c 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -63,7 +63,8 @@ impl AsRef<[u8]> for CollectionManagerError { /// # Methods /// /// - `owner_of`: Retrieve the owner of a specific asset within a collection. -pub trait Erc721 { +/// - `transfer_from`: Transfers the ownership of a asset from one address to another address. +pub trait Erc721 { /// Retrieves the owner of a specific asset within the specified collection. /// /// # Arguments @@ -75,6 +76,21 @@ pub trait Erc721 { /// /// The Ethereum address (`H160`) of the asset's owner or an error. fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; + + /// Transfers the ownership of a asset from one address to another address + /// + /// # Arguments + /// + /// * `collection_id` - The unique identifier for the collection. + /// * `from` - The current owner of the asset. + /// * `to` - The new owner. + /// * `asset_id` - The unique identifier for the asset within the collection. + fn transfer_from( + collection_id: CollectionId, + from: AccountId, + to: AccountId, + asset_id: U256, + ) -> Result<(), Erc721Error>; } /// Errors that can occur when interacting with ERC721 tokens. diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index eee08a80..b40ff98a 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -18,6 +18,8 @@ pub enum Action { TokenURI = "tokenURI(uint256)", /// Owner of OwnerOf = "ownerOf(uint256)", + /// Transfer from + TransferFrom = "transferFrom(address,address,uint256)", } /// Wrapper for the precompile function. @@ -27,14 +29,14 @@ pub struct Erc721Precompile( where AddressMapping: pallet_evm::AddressMapping, AccountId: Encode + Debug, - AssetManager: Erc721; + AssetManager: Erc721; impl Precompile for Erc721Precompile where AddressMapping: pallet_evm::AddressMapping, AccountId: Encode + Debug, - AssetManager: Erc721, + AssetManager: Erc721, { fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { let selector = handle.read_selector()?; @@ -42,6 +44,7 @@ where handle.check_function_modifier(match selector { Action::TokenURI => FunctionModifier::View, Action::OwnerOf => FunctionModifier::View, + Action::TransferFrom => FunctionModifier::NonPayable, })?; match selector { @@ -62,6 +65,7 @@ where Err(err) => Err(revert(err)), } }, + Action::TransferFrom => Err(revert("not implemented")), } } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 3adf2abf..a813d21f 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -12,13 +12,15 @@ type AddressMapping = pallet_evm::IdentityAddressMapping; fn check_selectors() { assert_eq!(Action::OwnerOf as u32, 0x6352211E); assert_eq!(Action::TokenURI as u32, 0xC87B56DD); + assert_eq!(Action::TransferFrom as u32, 0x23b872dd); } #[test] fn owner_of_asset_should_return_an_address() { impl_precompile_mock_simple!( Mock, - Ok(H160::from_str("ff00000000000000000000000000000012345678").unwrap()) + Ok(H160::from_str("ff00000000000000000000000000000012345678").unwrap()), + Ok(()) ); let owner_of_asset_4 = @@ -39,7 +41,7 @@ fn owner_of_asset_should_return_an_address() { #[test] fn if_mock_fails_should_return_the_error() { - impl_precompile_mock_simple!(Mock, Err(Erc721Error::UnexistentCollection)); + impl_precompile_mock_simple!(Mock, Err(Erc721Error::UnexistentCollection), Ok(())); let owner_of_asset_4 = hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") @@ -53,7 +55,7 @@ fn if_mock_fails_should_return_the_error() { #[test] fn invalid_contract_address_should_error() { - impl_precompile_mock_simple!(Mock, Ok(H160::zero())); + impl_precompile_mock_simple!(Mock, Ok(H160::zero()), Ok(())); let mut handle = create_mock_handle_from_input(Vec::new()); handle.code_address = H160::zero(); @@ -62,6 +64,51 @@ fn invalid_contract_address_should_error() { assert_eq!(result.unwrap_err(), revert("tried to parse selector out of bounds"),); } +mod transfer_from { + use super::*; + + #[test] + fn invalid_asset_id_should_fails() { + todo!("todo") + } + #[test] + fn sender_is_not_current_owner_should_fails() { + todo!("todo") + } + #[test] + fn receiver_is_the_current_owner_should_fails() { + impl_precompile_mock_simple!( + Mock, + Ok(H160::from_str("ff00000000000000000000000000000012345678").unwrap()), + Ok(()) + ); + + let selector = "23b872dd"; + let from = "ff00000000000000000000000000000012345678"; + let to = "ff00000000000000000000000000000012345678"; + let asset_id = format!("{:064x}", 4); // Pads to 64 characters. + let transfer_from_input = format!("{}{}{}{}", selector, from, to, asset_id); + let transfer_from_input = hex::decode(transfer_from_input).unwrap(); + let mut handle = create_mock_handle_from_input(transfer_from_input); + handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("not implemented"),); + } + #[test] + fn receiver_is_the_zero_address_should_fails() { + todo!("todo") + } + #[test] + fn send_value_as_money_should_fails() { + todo!("todo") + } + #[test] + fn sucessful_transfer_should_work() { + // TODO return new owner + todo!("todo") + } +} mod helpers { /// Macro to define a precompile mock with custom closures for testing. /// @@ -72,7 +119,7 @@ mod helpers { /// # Arguments /// /// * `$name`: An identifier to name the precompile mock type. - /// * `$owner_of_collection`: A closure that takes `collection_id` and `asset_id` and returns a `Result`. + /// * `$owner_of`: A closure that takes `collection_id` and `asset_id` and returns a `Result`. /// /// # Example /// @@ -84,15 +131,24 @@ mod helpers { /// ``` #[macro_export] macro_rules! impl_precompile_mock { - ($name:ident, $owner_of_collection:expr) => { + ($name:ident, $owner_of:expr, $transfer_from:expr) => { struct Erc721Mock; - impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { + impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { fn owner_of( - collectio_id: CollectionId, + collection_id: CollectionId, asset_id: U256, ) -> Result { - ($owner_of_collection)(collectio_id, asset_id) + ($owner_of)(collection_id, asset_id) + } + + fn transfer_from( + collection_id: CollectionId, + from: AccountId, + to: AccountId, + asset_id: U256, + ) -> Result<(), Erc721Error> { + ($transfer_from)(collection_id, from, to, asset_id) } } @@ -109,7 +165,7 @@ mod helpers { /// # Arguments /// /// * `$name`: An identifier to name the precompile mock type. - /// * `$owner_of_collection`: An expression that evaluates to a `Result`. + /// * `$owner_of`: An expression that evaluates to a `Result`. /// /// # Example /// @@ -118,8 +174,12 @@ mod helpers { /// ``` #[macro_export] macro_rules! impl_precompile_mock_simple { - ($name:ident, $owner_of_collection:expr) => { - impl_precompile_mock!($name, |_asset_id, _collection_id| { $owner_of_collection }); + ($name:ident, $owner_of:expr, $transfer_from:expr) => { + impl_precompile_mock!( + $name, + |_asset_id, _collection_id| { $owner_of }, + |_collection_id, _from, _to, _asset_id| { $transfer_from } + ); }; } } From 373e1ba24d6e44945065883de2c341f65cf0497a Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 13:54:34 +0200 Subject: [PATCH 086/114] traits have Error assiciated tyope --- pallets/living-assets-ownership/src/lib.rs | 51 ++++++++++++++++--- pallets/living-assets-ownership/src/traits.rs | 48 +++-------------- 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 729ab73d..a075a921 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -85,8 +85,28 @@ pub mod pallet { } } } + /// Errors that can occur when managing collections. + /// + /// - `CollectionIdOverflow`: The ID for the new collection would overflow. + /// - `UnknownError`: An unspecified error occurred. + #[derive(Debug, PartialEq)] + pub enum CollectionManagerError { + CollectionIdOverflow, + UnknownError, + } + + impl AsRef<[u8]> for CollectionManagerError { + fn as_ref(&self) -> &[u8] { + match self { + CollectionManagerError::CollectionIdOverflow => b"CollectionIdOverflow", + CollectionManagerError::UnknownError => b"UnknownError", + } + } + } impl traits::CollectionManager for Pallet { + type Error = CollectionManagerError; + fn base_uri(collection_id: CollectionId) -> Option { CollectionBaseURI::::get(collection_id) } @@ -94,27 +114,42 @@ pub mod pallet { fn create_collection( owner: T::AccountId, base_uri: BaseURI, - ) -> Result { + ) -> Result { match Self::do_create_collection(owner, base_uri) { Ok(collection_id) => Ok(collection_id), Err(err) => match err { Error::CollectionIdOverflow => { - Err(traits::CollectionManagerError::CollectionIdOverflow) + Err(CollectionManagerError::CollectionIdOverflow) }, - _ => Err(traits::CollectionManagerError::UnknownError), + _ => Err(CollectionManagerError::UnknownError), }, } } } + /// Errors that can occur when interacting with ERC721 tokens. + /// + /// - `UnexistentCollection`: The specified collection does not exist. + #[derive(Debug, PartialEq)] + pub enum Erc721Error { + UnexistentCollection, + } + + impl AsRef<[u8]> for Erc721Error { + fn as_ref(&self) -> &[u8] { + match self { + Erc721Error::UnexistentCollection => b"UnexistentCollection", + } + } + } + impl traits::Erc721 for Pallet { - fn owner_of( - collection_id: CollectionId, - asset_id: U256, - ) -> Result { + type Error = Erc721Error; + + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { match CollectionBaseURI::::get(collection_id) { Some(_) => Ok(convert_asset_id_to_owner(asset_id)), - None => Err(traits::Erc721Error::UnexistentCollection), + None => Err(Erc721Error::UnexistentCollection), } } } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index f2a677ee..5cae4afe 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -11,6 +11,8 @@ use sp_core::{H160, U256}; /// - `owner_of_collection`: Retrieve the owner of a specified collection. /// - `create_collection`: Create a new collection and assign it to an owner. pub trait CollectionManager { + type Error; + /// Retrieves the base uri of the specified collection. /// /// # Arguments @@ -31,29 +33,7 @@ pub trait CollectionManager { /// # Returns /// /// A result containing the `collection_id` of the newly created collection or an error. - fn create_collection( - owner: AccountId, - base_uri: BaseURI, - ) -> Result; -} - -/// Errors that can occur when managing collections. -/// -/// - `CollectionIdOverflow`: The ID for the new collection would overflow. -/// - `UnknownError`: An unspecified error occurred. -#[derive(Debug, PartialEq)] -pub enum CollectionManagerError { - CollectionIdOverflow, - UnknownError, -} - -impl AsRef<[u8]> for CollectionManagerError { - fn as_ref(&self) -> &[u8] { - match self { - CollectionManagerError::CollectionIdOverflow => b"CollectionIdOverflow", - CollectionManagerError::UnknownError => b"UnknownError", - } - } + fn create_collection(owner: AccountId, base_uri: BaseURI) -> Result; } /// The `Erc721` trait provides an interface for handling ERC721 tokens in a blockchain environment. @@ -64,6 +44,8 @@ impl AsRef<[u8]> for CollectionManagerError { /// /// - `owner_of`: Retrieve the owner of a specific asset within a collection. pub trait Erc721 { + type Error; + /// Retrieves the owner of a specific asset within the specified collection. /// /// # Arguments @@ -74,23 +56,7 @@ pub trait Erc721 { /// # Returns /// /// The Ethereum address (`H160`) of the asset's owner or an error. - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; -} - -/// Errors that can occur when interacting with ERC721 tokens. -/// -/// - `UnexistentCollection`: The specified collection does not exist. -#[derive(Debug, PartialEq)] -pub enum Erc721Error { - UnexistentCollection, -} - -impl AsRef<[u8]> for Erc721Error { - fn as_ref(&self) -> &[u8] { - match self { - Erc721Error::UnexistentCollection => b"UnexistentCollection", - } - } + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; } #[cfg(test)] @@ -98,7 +64,7 @@ mod tests { use frame_support::{assert_err, assert_ok}; use super::*; - use crate::{mock::*, Event}; + use crate::{mock::*, Erc721Error, Event}; type AccountId = ::AccountId; const ALICE: AccountId = 0x1234; From 990d44c0b5a2207616382e43c164eddde5b7c99c Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 14:01:18 +0200 Subject: [PATCH 087/114] more generiuc signature --- pallets/living-assets-ownership/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index a075a921..b0678726 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -114,7 +114,7 @@ pub mod pallet { fn create_collection( owner: T::AccountId, base_uri: BaseURI, - ) -> Result { + ) -> Result { match Self::do_create_collection(owner, base_uri) { Ok(collection_id) => Ok(collection_id), Err(err) => match err { @@ -146,7 +146,7 @@ pub mod pallet { impl traits::Erc721 for Pallet { type Error = Erc721Error; - fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { + fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { match CollectionBaseURI::::get(collection_id) { Some(_) => Ok(convert_asset_id_to_owner(asset_id)), None => Err(Erc721Error::UnexistentCollection), From e9d5b1e86935c1728289906daaa322dfe7b273d8 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 14:13:02 +0200 Subject: [PATCH 088/114] fix compilation --- pallets/living-assets-ownership/src/traits.rs | 4 ++-- precompile/erc721/src/tests.rs | 10 ++++++---- precompile/living-assets/src/tests.rs | 14 ++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 5cae4afe..e686d4c4 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -11,7 +11,7 @@ use sp_core::{H160, U256}; /// - `owner_of_collection`: Retrieve the owner of a specified collection. /// - `create_collection`: Create a new collection and assign it to an owner. pub trait CollectionManager { - type Error; + type Error: AsRef<[u8]>; /// Retrieves the base uri of the specified collection. /// @@ -44,7 +44,7 @@ pub trait CollectionManager { /// /// - `owner_of`: Retrieve the owner of a specific asset within a collection. pub trait Erc721 { - type Error; + type Error: AsRef<[u8]>; /// Retrieves the owner of a specific asset within the specified collection. /// diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 4aef1f13..b31f2768 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use super::*; -use pallet_living_assets_ownership::{traits::Erc721Error, CollectionId}; +use pallet_living_assets_ownership::CollectionId; use precompile_utils::testing::create_mock_handle_from_input; use sp_core::{H160, U256}; @@ -39,7 +39,7 @@ fn owner_of_asset_should_return_an_address() { #[test] fn if_mock_fails_should_return_the_error() { - impl_precompile_mock_simple!(Mock, Err(Erc721Error::UnexistentCollection)); + impl_precompile_mock_simple!(Mock, Err("this is an error")); let owner_of_asset_4 = hex::decode("6352211e0000000000000000000000000000000000000000000000000000000000000004") @@ -48,7 +48,7 @@ fn if_mock_fails_should_return_the_error() { handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert(Erc721Error::UnexistentCollection)); + assert_eq!(result.unwrap_err(), revert("this is an error")); } #[test] @@ -107,10 +107,12 @@ mod helpers { struct Erc721Mock; impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { + type Error = &'static str; + fn owner_of( collectio_id: CollectionId, asset_id: U256, - ) -> Result { + ) -> Result { ($owner_of_collection)(collectio_id, asset_id) } } diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index aa68a48b..2ef9ffac 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -4,7 +4,7 @@ #![allow(clippy::redundant_closure_call)] use super::*; -use pallet_living_assets_ownership::{traits::CollectionManagerError, BaseURI}; +use pallet_living_assets_ownership::BaseURI; use precompile_utils::{ revert, succeed, testing::{create_mock_handle, create_mock_handle_from_input}, @@ -32,16 +32,12 @@ fn check_log_selectors() { #[test] fn failing_create_collection_should_return_error() { - impl_precompile_mock_simple!( - Mock, - Err(CollectionManagerError::UnknownError), - Some(BaseURI::new()) - ); + impl_precompile_mock_simple!(Mock, Err("this is an error"), Some(BaseURI::new())); let mut handle = create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); let result = Mock::execute(&mut handle); - assert_eq!(result.unwrap_err(), revert(CollectionManagerError::UnknownError)); + assert_eq!(result.unwrap_err(), revert("this is an error")); } #[test] @@ -154,10 +150,12 @@ mod helpers { impl pallet_living_assets_ownership::traits::CollectionManager for CollectionManagerMock { + type Error = &'static str; + fn create_collection( owner: AccountId, base_uri: BaseURI, - ) -> Result { + ) -> Result { ($create_collection_result)(owner, base_uri) } From e76a68065f3b85a74628f2fdca042f323eb319ed Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Wed, 16 Aug 2023 14:20:29 +0200 Subject: [PATCH 089/114] test moved where concreate impl is tested --- pallets/living-assets-ownership/src/tests.rs | 124 +++++++++++++++++ pallets/living-assets-ownership/src/traits.rs | 126 ------------------ 2 files changed, 124 insertions(+), 126 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 16e3b0d2..36ec27ce 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -118,3 +118,127 @@ fn test_is_collection_address_invalid() { assert!(!is_collection_address(invalid_address)); } + +mod traits { + use super::*; + use crate::{ + traits::{CollectionManager, Erc721}, + Erc721Error, Event, + }; + use frame_support::{assert_err, assert_ok}; + + #[test] + fn base_uri_of_unexistent_collection_is_none() { + new_test_ext().execute_with(|| { + assert_eq!(>::base_uri(0), None); + assert_eq!(>::base_uri(1), None); + }); + } + + #[test] + fn create_new_collection_should_emit_an_event() { + new_test_ext().execute_with(|| { + // Go past genesis block so events get deposited + System::set_block_number(1); + + assert_ok!(>::create_collection( + ALICE, + BaseURI::default(), + )); + System::assert_last_event( + Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), + ); + }); + } + + #[test] + fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { + new_test_ext().execute_with(|| { + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 0 + ); + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 1 + ); + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 2 + ); + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 3 + ); + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 4 + ); + assert_eq!( + >::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), + 5 + ); + }); + } + + #[test] + fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { + let base_uri = BaseURI::try_from("https://example.com/".as_bytes().to_vec()).unwrap(); + + new_test_ext().execute_with(|| { + assert_ok!(>::create_collection( + ALICE, + base_uri.clone() + )); + assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); + }); + } + + #[test] + fn owner_of_asset_of_unexistent_collection_should_error() { + new_test_ext().execute_with(|| { + let result = ::owner_of(0, 2.into()); + assert_err!(result, Erc721Error::UnexistentCollection); + }); + } + + #[test] + fn erc721_owner_of_asset_of_collection() { + new_test_ext().execute_with(|| { + let collection_id = + >::create_collection( + ALICE, + BaseURI::default(), + ) + .unwrap(); + assert_eq!( + ::owner_of(collection_id, 2.into()).unwrap(), + H160::from_low_u64_be(0x0000000000000002) + ); + }); + } +} diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index e686d4c4..771c98c6 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -58,129 +58,3 @@ pub trait Erc721 { /// The Ethereum address (`H160`) of the asset's owner or an error. fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result; } - -#[cfg(test)] -mod tests { - use frame_support::{assert_err, assert_ok}; - - use super::*; - use crate::{mock::*, Erc721Error, Event}; - - type AccountId = ::AccountId; - const ALICE: AccountId = 0x1234; - - #[test] - fn base_uri_of_unexistent_collection_is_none() { - new_test_ext().execute_with(|| { - assert_eq!(>::base_uri(0), None); - assert_eq!(>::base_uri(1), None); - }); - } - - #[test] - fn create_new_collection_should_emit_an_event() { - new_test_ext().execute_with(|| { - // Go past genesis block so events get deposited - System::set_block_number(1); - - assert_ok!(>::create_collection( - ALICE, - BaseURI::default(), - )); - System::assert_last_event( - Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), - ); - }); - } - - #[test] - fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { - new_test_ext().execute_with(|| { - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 0 - ); - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 1 - ); - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 2 - ); - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 3 - ); - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 4 - ); - assert_eq!( - >::create_collection( - ALICE, - BaseURI::default() - ) - .unwrap(), - 5 - ); - }); - } - - #[test] - fn living_assets_ownership_trait_should_set_base_uri_when_creating_new_collection() { - let base_uri = BaseURI::try_from("https://example.com/".as_bytes().to_vec()).unwrap(); - - new_test_ext().execute_with(|| { - assert_ok!(>::create_collection( - ALICE, - base_uri.clone() - )); - assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); - }); - } - - #[test] - fn owner_of_asset_of_unexistent_collection_should_error() { - new_test_ext().execute_with(|| { - let result = ::owner_of(0, 2.into()); - assert_err!(result, Erc721Error::UnexistentCollection); - }); - } - - #[test] - fn erc721_owner_of_asset_of_collection() { - new_test_ext().execute_with(|| { - let collection_id = - >::create_collection( - ALICE, - BaseURI::default(), - ) - .unwrap(); - assert_eq!( - ::owner_of(collection_id, 2.into()).unwrap(), - H160::from_low_u64_be(0x0000000000000002) - ); - }); - } -} From 77120b42cad9f2389f07d4b55ac445d6fade0d7e Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 17 Aug 2023 10:19:32 +0200 Subject: [PATCH 090/114] removed harcoded buffer from tests --- precompile/living-assets/src/tests.rs | 52 ++++++++++++++++----------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 3abcb590..9403d4eb 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -4,6 +4,7 @@ #![allow(clippy::redundant_closure_call)] use super::*; +use frame_support::assert_ok; use pallet_living_assets_ownership::BaseURI; use precompile_utils::{ revert, succeed, @@ -15,8 +16,6 @@ use sp_std::vec::Vec; type AccountId = H160; type AddressMapping = pallet_evm::IdentityAddressMapping; -const CREATE_COLLECTION_WITH_URI: &str = "059dfe1300000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010697066733a2f2f636172626f6e61726100000000000000000000000000000000"; - #[test] fn check_selectors() { assert_eq!(Action::CreateCollection as u32, 0x059dfe13); @@ -34,8 +33,11 @@ fn check_log_selectors() { fn failing_create_collection_should_return_error() { impl_precompile_mock_simple!(Mock, Err("this is an error"), Some(BaseURI::new())); - let mut handle = - create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let input = EvmDataWriter::new_with_selector(Action::CreateCollection) + .write(Bytes("ipfs::/carbonara".into())) + .build(); + let mut handle = create_mock_handle_from_input(input); + let result = Mock::execute(&mut handle); assert_eq!(result.unwrap_err(), revert("this is an error")); } @@ -44,12 +46,14 @@ fn failing_create_collection_should_return_error() { fn create_collection_should_return_address() { impl_precompile_mock_simple!(Mock, Ok(5), Some(BaseURI::new())); - let mut handle = - create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let input = EvmDataWriter::new_with_selector(Action::CreateCollection) + .write(Bytes("ipfs::/carbonara".into())) + .build(); + let mut handle = create_mock_handle_from_input(input); + let result = Mock::execute(&mut handle); - assert!(result.is_ok()); - assert_eq!( - result.unwrap(), + assert_ok!( + result, succeed( hex::decode("000000000000000000000000ffffffffffffffffffffffff0000000000000005") .unwrap() @@ -61,8 +65,11 @@ fn create_collection_should_return_address() { fn create_collection_should_generate_log() { impl_precompile_mock_simple!(Mock, Ok(0xffff), Some(BaseURI::new())); - let mut handle = - create_mock_handle_from_input(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap()); + let input = EvmDataWriter::new_with_selector(Action::CreateCollection) + .write(Bytes("ipfs::/carbonara".into())) + .build(); + let mut handle = create_mock_handle_from_input(input); + let result = Mock::execute(&mut handle); assert!(result.is_ok()); let logs = handle.logs; @@ -80,8 +87,12 @@ fn create_collection_should_generate_log() { #[test] fn create_collection_on_mock_with_nonzero_value_fails() { impl_precompile_mock_simple!(Mock, Ok(5), Some(BaseURI::new())); - let mut handle = - create_mock_handle(hex::decode(CREATE_COLLECTION_WITH_URI).unwrap(), 0, 1, H160::zero()); + + let input = EvmDataWriter::new_with_selector(Action::CreateCollection) + .write(Bytes("ipfs::/carbonara".into())) + .build(); + let mut handle = create_mock_handle(input, 0, 1, H160::zero()); + let result = Mock::execute(&mut handle); assert!(result.is_err()); assert_eq!(result.unwrap_err(), revert("function is not payable")); @@ -91,10 +102,10 @@ fn create_collection_on_mock_with_nonzero_value_fails() { fn create_collection_assign_collection_to_caller() { impl_precompile_mock!( Mock, // name of the defined precompile - |owner, base_uri| { + |owner, base_uri: BaseURI| { assert_eq!(owner, H160::from_low_u64_be(0x1234)); assert_eq!( - base_uri, + base_uri.to_ascii_lowercase(), vec![105, 112, 102, 115, 58, 47, 47, 99, 97, 114, 98, 111, 110, 97, 114, 97] ); Ok(0) @@ -102,12 +113,11 @@ fn create_collection_assign_collection_to_caller() { |_| { Some(BaseURI::new()) } // Closure for owner_of_collection result ); - let mut handle = create_mock_handle( - hex::decode(CREATE_COLLECTION_WITH_URI).unwrap(), - 0, - 0, - H160::from_low_u64_be(0x1234), - ); + let input = EvmDataWriter::new_with_selector(Action::CreateCollection) + .write(Bytes("ipfs://carbonara".into())) + .build(); + + let mut handle = create_mock_handle(input, 0, 0, H160::from_low_u64_be(0x1234)); let result = Mock::execute(&mut handle); assert!(result.is_ok()); } From 01d7687535b40a5ed59afde56bd528a52adfd018 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 17 Aug 2023 10:25:12 +0200 Subject: [PATCH 091/114] refactoring --- precompile/living-assets/src/tests.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/precompile/living-assets/src/tests.rs b/precompile/living-assets/src/tests.rs index 9403d4eb..98618bab 100644 --- a/precompile/living-assets/src/tests.rs +++ b/precompile/living-assets/src/tests.rs @@ -104,10 +104,7 @@ fn create_collection_assign_collection_to_caller() { Mock, // name of the defined precompile |owner, base_uri: BaseURI| { assert_eq!(owner, H160::from_low_u64_be(0x1234)); - assert_eq!( - base_uri.to_ascii_lowercase(), - vec![105, 112, 102, 115, 58, 47, 47, 99, 97, 114, 98, 111, 110, 97, 114, 97] - ); + assert_eq!(base_uri.escape_ascii().to_string(), "ipfs://carbonara"); Ok(0) }, // Closure for create_collection result |_| { Some(BaseURI::new()) } // Closure for owner_of_collection result From 2396f85d6c7aa545def98a91c310f9068d24af39 Mon Sep 17 00:00:00 2001 From: Alessandro Siniscalchi Date: Thu, 17 Aug 2023 10:40:33 +0200 Subject: [PATCH 092/114] checking for BaseURI bounds --- pallets/living-assets-ownership/src/tests.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 36ec27ce..0801816e 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -11,6 +11,11 @@ type AccountId = ::AccountId; const ALICE: AccountId = 0x1234; +#[test] +fn max_length_of_collection_base_uri_should_be_255() { + assert_eq!(BaseURI::bound(), 255); +} + #[test] fn base_uri_unexistent_collection_is_none() { new_test_ext().execute_with(|| { From 48a0b3598f819b5b2de03b71844a5b601a7bdc35 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 17 Aug 2023 12:09:50 +0200 Subject: [PATCH 093/114] add transfer_from sender and receiver checks --- precompile/erc721/src/lib.rs | 53 ++++++++++++++++++++------ precompile/erc721/src/tests.rs | 68 ++++++++++++++++++++++++++-------- 2 files changed, 93 insertions(+), 28 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index b40ff98a..e4bfa858 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -1,14 +1,15 @@ //! Living Assets precompile module. #![cfg_attr(not(feature = "std"), no_std)] -use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; +use fp_evm::{Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput}; +use frame_support::pallet_prelude::*; use pallet_living_assets_ownership::{address_to_collection_id, traits::Erc721}; use parity_scale_codec::Encode; use precompile_utils::{ revert, succeed, Address, EvmDataWriter, EvmResult, FunctionModifier, PrecompileHandleExt, }; -use sp_core::U256; +use sp_core::{H160, U256}; use sp_std::{fmt::Debug, marker::PhantomData}; #[precompile_utils_macro::generate_function_selector] @@ -50,22 +51,50 @@ where match selector { Action::TokenURI => Err(revert("not implemented")), Action::OwnerOf => { + // get input data let mut input = handle.read_input()?; input.expect_arguments(1)?; + let asset_id: U256 = input.read()?; + + let owner = Self::owner_of(asset_id, handle.code_address())?; + Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())) + }, + Action::TransferFrom => { + // get input data + let mut input = handle.read_input()?; + input.expect_arguments(3)?; + let from: H160 = input.read::
()?.into(); + let to: H160 = input.read::
()?.into(); let asset_id: U256 = input.read()?; - // collection id is encoded into the contract address - let collection_id = match address_to_collection_id(handle.code_address()) { - Ok(collection_id) => collection_id, - Err(_) => return Err(revert("invalid collection address")), - }; - match AssetManager::owner_of(collection_id, asset_id) { - Ok(owner) => Ok(succeed(EvmDataWriter::new().write(Address(owner)).build())), - Err(err) => Err(revert(err)), - } + // checks + let owner = Self::owner_of(asset_id, handle.code_address())?; + ensure!(owner == from, revert("sender must be the current owner")); + ensure!(from != to, revert("sender and receiver cannot be the same")); + + Ok((succeed(EvmDataWriter::new().write(asset_id).build())).into()) }, - Action::TransferFrom => Err(revert("not implemented")), + } + } +} + +impl + Erc721Precompile +where + AddressMapping: pallet_evm::AddressMapping, + AccountId: Encode + Debug, + AssetManager: Erc721, +{ + fn owner_of(asset_id: U256, code_address: H160) -> Result { + // collection id is encoded into the contract address + let collection_id = match address_to_collection_id(code_address) { + Ok(collection_id) => collection_id, + Err(_) => return Err(revert("invalid collection address")), + }; + match AssetManager::owner_of(collection_id, asset_id) { + Ok(owner) => Ok(owner), + Err(err) => Err(revert(err)), } } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index a813d21f..248db7bc 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -68,41 +68,77 @@ mod transfer_from { use super::*; #[test] - fn invalid_asset_id_should_fails() { + fn invalid_asset_id_should_fail() { todo!("todo") } #[test] - fn sender_is_not_current_owner_should_fails() { - todo!("todo") + fn sender_is_not_current_owner_should_fail() { + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), + // transfer_from result + Ok(()) + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0xBB); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle_from_input(input_data); + handle.code_address = contract_address.unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("sender must be the current owner"),); } + #[test] - fn receiver_is_the_current_owner_should_fails() { + fn receiver_is_the_current_owner_should_fail() { impl_precompile_mock_simple!( Mock, - Ok(H160::from_str("ff00000000000000000000000000000012345678").unwrap()), + // owner_of result + Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // transfer_from result Ok(()) ); - let selector = "23b872dd"; - let from = "ff00000000000000000000000000000012345678"; - let to = "ff00000000000000000000000000000012345678"; - let asset_id = format!("{:064x}", 4); // Pads to 64 characters. - let transfer_from_input = format!("{}{}{}{}", selector, from, to, asset_id); - let transfer_from_input = hex::decode(transfer_from_input).unwrap(); - let mut handle = create_mock_handle_from_input(transfer_from_input); - handle.code_address = H160::from_str("ffffffffffffffffffffffff0000000000000005").unwrap(); + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0xAA); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle_from_input(input_data); + handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("not implemented"),); + assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same"),); } + #[test] - fn receiver_is_the_zero_address_should_fails() { + fn receiver_is_the_zero_address_should_fail() { todo!("todo") } + #[test] - fn send_value_as_money_should_fails() { + fn send_value_as_money_should_fail() { todo!("todo") } + #[test] fn sucessful_transfer_should_work() { // TODO return new owner From c98517e3ac1aa63f6693cef7c97cdebf0518308b Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 17 Aug 2023 12:20:29 +0200 Subject: [PATCH 094/114] add dummy erc721 impl to pallet for compiling --- pallets/living-assets-ownership/src/lib.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 23e5c39a..35480c10 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -87,16 +87,15 @@ pub mod pallet { match Self::do_create_collection(owner) { Ok(collection_id) => Ok(collection_id), Err(err) => match err { - Error::CollectionIdOverflow => { - Err(traits::CollectionManagerError::CollectionIdOverflow) - }, + Error::CollectionIdOverflow => + Err(traits::CollectionManagerError::CollectionIdOverflow), _ => Err(traits::CollectionManagerError::UnknownError), }, } } } - impl traits::Erc721 for Pallet { + impl traits::Erc721 for Pallet { fn owner_of( collection_id: CollectionId, asset_id: U256, @@ -106,6 +105,15 @@ pub mod pallet { None => Err(traits::Erc721Error::UnexistentCollection), } } + + fn transfer_from( + collection_id: CollectionId, + from: T::AccountId, + to: T::AccountId, + asset_id: U256, + ) -> Result<(), traits::Erc721Error> { + Ok(()) + } } } @@ -161,7 +169,7 @@ pub fn collection_id_to_address(collection_id: CollectionId) -> H160 { /// * A `Result` which is either the `CollectionId` or an error indicating the address is invalid. pub fn address_to_collection_id(address: H160) -> Result { if &address.0[0..12] != ASSET_PRECOMPILE_ADDRESS_PREFIX { - return Err(CollectionError::InvalidPrefix); + return Err(CollectionError::InvalidPrefix) } let id_bytes: [u8; 8] = address.0[12..].try_into().unwrap(); Ok(CollectionId::from_be_bytes(id_bytes)) From a5c8e783addf5299fe5efae2f9e53f92f1c05c73 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 17 Aug 2023 17:36:32 +0200 Subject: [PATCH 095/114] erc721 transfer_from tests --- precompile/erc721/src/lib.rs | 1 + precompile/erc721/src/tests.rs | 60 ++++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index e4bfa858..476e9262 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -72,6 +72,7 @@ where let owner = Self::owner_of(asset_id, handle.code_address())?; ensure!(owner == from, revert("sender must be the current owner")); ensure!(from != to, revert("sender and receiver cannot be the same")); + ensure!(to != H160::zero(), revert("receiver cannot be zero address")); Ok((succeed(EvmDataWriter::new().write(asset_id).build())).into()) }, diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index f95c0c11..e63592ca 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -84,12 +84,10 @@ fn token_owners_should_have_at_least_token_id_as_argument() { } mod transfer_from { + use precompile_utils::testing::create_mock_handle; + use super::*; - #[test] - fn invalid_asset_id_should_fail() { - todo!("todo") - } #[test] fn sender_is_not_current_owner_should_fail() { impl_precompile_mock_simple!( @@ -145,17 +143,65 @@ mod transfer_from { handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same"),); + assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same")); } #[test] fn receiver_is_the_zero_address_should_fail() { - todo!("todo") + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // transfer_from result + Ok(()) + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0x0); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle_from_input(input_data); + handle.code_address = contract_address.unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("receiver cannot be zero address")); } #[test] fn send_value_as_money_should_fail() { - todo!("todo") + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // transfer_from result + Ok(()) + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0x0); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle(input_data, 0, 1, H160::zero()); + handle.code_address = contract_address.unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("function is not payable")); } #[test] From f121c5cdfd4219da611e2fd7085d12c5f88023be Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 17 Aug 2023 18:40:51 +0200 Subject: [PATCH 096/114] erc721 precompile final tests --- precompile/erc721/src/lib.rs | 35 +++++++++++++++++++++- precompile/erc721/src/tests.rs | 54 ++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 476e9262..63e09a0a 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -74,7 +74,9 @@ where ensure!(from != to, revert("sender and receiver cannot be the same")); ensure!(to != H160::zero(), revert("receiver cannot be zero address")); - Ok((succeed(EvmDataWriter::new().write(asset_id).build())).into()) + Self::transfer_from(handle.code_address(), from, to, asset_id)?; + + Ok((succeed(vec![])).into()) }, } } @@ -100,5 +102,36 @@ where } } +impl + Erc721Precompile +where + AddressMapping: pallet_evm::AddressMapping, + AccountId: Encode + Debug, + AssetManager: Erc721, +{ + fn transfer_from( + code_address: H160, + from: H160, + to: H160, + asset_id: U256, + ) -> Result<(), PrecompileFailure> { + // collection id is encoded into the contract address + let collection_id = match address_to_collection_id(code_address) { + Ok(collection_id) => collection_id, + Err(_) => return Err(revert("invalid collection address")), + }; + + match AssetManager::transfer_from( + collection_id, + AddressMapping::into_account_id(from), + AddressMapping::into_account_id(to), + asset_id, + ) { + Ok(_) => Ok(()), + Err(err) => Err(revert(err)), + } + } +} + #[cfg(test)] mod tests; diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index e63592ca..adaf3d79 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -84,6 +84,7 @@ fn token_owners_should_have_at_least_token_id_as_argument() { } mod transfer_from { + use frame_support::assert_ok; use precompile_utils::testing::create_mock_handle; use super::*; @@ -206,8 +207,57 @@ mod transfer_from { #[test] fn sucessful_transfer_should_work() { - // TODO return new owner - todo!("todo") + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // transfer_from result + Ok(()) + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0xBB); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle_from_input(input_data); + handle.code_address = contract_address.unwrap(); + assert_ok!(Mock::execute(&mut handle)); + } + #[test] + fn unsucessful_transfer_should_fail() { + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // transfer_from result + Err("this is an error") + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0xBB); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle_from_input(input_data); + handle.code_address = contract_address.unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("this is an error")); } } mod helpers { From 5b0ee33bee9ae07f960a25568eeb96daaaa7dfc6 Mon Sep 17 00:00:00 2001 From: magecnion Date: Fri, 18 Aug 2023 17:50:58 +0200 Subject: [PATCH 097/114] add transfer_from func signature to sol contract --- precompile/erc721/contracts/IERC721.sol | 2 ++ precompile/erc721/src/lib.rs | 2 +- runtime/src/precompiles/mod.rs | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol index c12dcb51..3a54aca5 100644 --- a/precompile/erc721/contracts/IERC721.sol +++ b/precompile/erc721/contracts/IERC721.sol @@ -9,4 +9,6 @@ interface IERC721 { function tokenURI(uint256 _tokenId) external view returns (string memory); function ownerOf(uint256 _tokenId) external view returns (address); + + function transferFrom(address _from, address _to, uint256 _tokenId) external payable; } diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index d9d0e279..f5805bfc 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -7,7 +7,7 @@ use precompile_utils::{ }; use sp_core::{H160, U256}; -use sp_std::{fmt::Debug, marker::PhantomData}; +use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; #[precompile_utils_macro::generate_function_selector] #[derive(Debug, PartialEq)] diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 6563d5f5..6879212f 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -35,7 +35,10 @@ type LivingAssetsPrecompile = CollectionManagerPrecompile< pallet_living_assets_ownership::Pallet, >; -type Erc721 = Erc721Precompile>; +type Erc721 = Erc721Precompile< + pallet_living_assets_ownership::Pallet, + pallet_evm::HashedAddressMapping, +>; impl PrecompileSet for FrontierPrecompiles where From e857fb09ecc3c7933eb0832612bcc6914286a161 Mon Sep 17 00:00:00 2001 From: magecnion Date: Mon, 21 Aug 2023 13:38:33 +0200 Subject: [PATCH 098/114] WIP add getting initial owner for assets storage --- Cargo.lock | 4 + pallets/living-assets-ownership/Cargo.toml | 15 +- pallets/living-assets-ownership/src/lib.rs | 44 ++++- pallets/living-assets-ownership/src/mock.rs | 113 ++++++++++- pallets/living-assets-ownership/src/tests.rs | 188 ++++++++++++++++++- 5 files changed, 358 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ebe695c..3434b58d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7180,6 +7180,10 @@ dependencies = [ "frame-system", "hex", "hex-literal 0.4.1", + "pallet-balances", + "pallet-ethereum", + "pallet-evm", + "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index 73aaadac..e352e5e6 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -16,10 +16,16 @@ hex-literal = { workspace = true, optional = true} parity-scale-codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } +# Frontier +pallet-evm = { workspace = true } +pallet-ethereum = { workspace = true } + # Substrate frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } +pallet-balances = { workspace = true } +pallet-timestamp = { workspace = true } sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-std = { workspace = true } @@ -46,11 +52,18 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", + "pallet-balances/std", + "pallet-timestamp/std", "sp-arithmetic/std", "sp-core/std", "sp-std/std", + # Frontier FRAME + "pallet-ethereum/std", + "pallet-evm/std", ] try-runtime = [ "frame-system/try-runtime", - "frame-support/try-runtime" + "frame-support/try-runtime", + "pallet-evm/try-runtime", + "pallet-ethereum/try-runtime", ] diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 926c9e35..c3ac13f3 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -11,15 +11,20 @@ pub mod traits; #[frame_support::pallet] pub mod pallet { + use crate::functions::convert_asset_id_to_owner; use super::*; use frame_support::{ pallet_prelude::{OptionQuery, ValueQuery, *}, + storage::types::QueryKindTrait, BoundedVec, }; use frame_system::pallet_prelude::*; + use pallet_evm::{pallet, AddressMapping}; + // use pallet_evm::AddressMapping; use sp_core::{H160, U256}; + // use sp_runtime::{traits::AccountIdConversion, AccountId32}; /// Collection id type pub type CollectionId = u64; @@ -32,7 +37,7 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config { + pub trait Config: frame_system::Config + pallet_evm::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -59,6 +64,17 @@ pub mod pallet { pub(super) type CollectionBaseURI = StorageMap<_, Blake2_128Concat, CollectionId, BaseURI, OptionQuery>; + #[pallet::storage] + pub(super) type Asset = + StorageMap<_, Blake2_128Concat, U256, T::AccountId, OptionQuery>; + + fn asset(key: U256) -> ::AccountId { + Asset::::get(key).unwrap_or_else(|| { + let owner = convert_asset_id_to_owner(key); + ::AddressMapping::into_account_id(owner) + }) + } + /// Pallet events #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] @@ -66,6 +82,9 @@ pub mod pallet { /// Collection created /// parameters. [collection_id, who] CollectionCreated { collection_id: CollectionId, who: T::AccountId }, + /// Asset transferred to `who` + /// parameters. [asset_id_id, who] + AssetTransferred { asset_id: U256, receiver: T::AccountId }, } // Errors inform users that something went wrong. @@ -76,6 +95,7 @@ pub mod pallet { CollectionIdOverflow, /// Unexistent collection UnexistentCollection, + // SenderIsNotTheCurrentOwner, } impl AsRef<[u8]> for Error { @@ -103,7 +123,29 @@ pub mod pallet { Err(err) => Err(err.into()), } } + + #[pallet::call_index(1)] + #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO set proper weight + pub fn transfer_from( + origin: OriginFor, + from: T::AccountId, + to: T::AccountId, + asset_id: U256, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // checks + // ensure!(owner == from, revert("sender must be the current owner")); + // ensure!(from != to, revert("sender and receiver cannot be the same")); + // ensure!(to != H160::zero(), revert("receiver cannot be zero address")); + + Asset::::set(asset_id, Some(to.clone())); + Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to }); + + Ok(()) + } } + /// Errors that can occur when managing collections. /// /// - `CollectionIdOverflow`: The ID for the new collection would overflow. diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index b8ba0c9d..0633601c 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -1,9 +1,12 @@ use crate as pallet_livingassets_ownership; +use frame_support::parameter_types; use frame_support::traits::{ConstU16, ConstU64}; -use sp_core::{ConstU32, H256}; +use frame_support::weights::Weight; +use pallet_evm::{AddressMapping, EnsureAddressTruncated, FeeCalculator}; +use sp_core::{ConstU32, H160, H256, U256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, + AccountId32, BuildStorage, }; type Block = frame_system::mocking::MockBlock; @@ -15,6 +18,8 @@ frame_support::construct_runtime!( { System: frame_system, LivingAssetsModule: pallet_livingassets_ownership, + Balances: pallet_balances, + Timestamp: pallet_timestamp, } ); @@ -35,7 +40,7 @@ impl frame_system::Config for Test { type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); @@ -49,6 +54,108 @@ impl pallet_livingassets_ownership::Config for Test { type BaseURILimit = ConstU32<256>; } +parameter_types! { + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; + pub const ExistentialDeposit: u64 = 500; +} + +impl pallet_balances::Config for Test { + type MaxLocks = MaxLocks; + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type RuntimeHoldReason = (); + type WeightInfo = (); + type MaxReserves = (); + type ReserveIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = (); + type MaxFreezes = (); +} +parameter_types! { + pub const MinimumPeriod: u64 = 6000 / 2; +} + +impl pallet_timestamp::Config for Test { + type Moment = u64; + type OnTimestampSet = (); + type MinimumPeriod = MinimumPeriod; + type WeightInfo = (); +} +pub struct FixedGasPrice; +impl FeeCalculator for FixedGasPrice { + fn min_gas_price() -> (U256, Weight) { + (1.into(), Weight::zero()) + } +} +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; +parameter_types! { + pub const TransactionByteFee: u64 = 1; + pub const ChainId: u64 = 42; + pub const BlockGasLimit: U256 = U256::MAX; + pub WeightPerGas: Weight = Weight::from_parts(1, 0); + pub GasLimitPovSizeRatio: u64 = { + let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); + block_gas_limit.saturating_div(MAX_POV_SIZE) + }; +} + +pub struct HashedAddressMapping; + +impl AddressMapping for HashedAddressMapping { + fn into_account_id(address: H160) -> AccountId32 { + let mut data = [0u8; 32]; + data[0..20].copy_from_slice(&address[..]); + AccountId32::from(Into::<[u8; 32]>::into(data)) + } +} +// pub struct FindAuthorTruncated; +// impl FindAuthor for FindAuthorTruncated { +// fn find_author<'a, I>(_digests: I) -> Option +// where +// I: 'a + IntoIterator, +// { +// Some(address_build(0).address) +// } +// } +impl pallet_evm::Config for Test { + type FeeCalculator = FixedGasPrice; + type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type WeightPerGas = WeightPerGas; + type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; + type CallOrigin = EnsureAddressTruncated; + type WithdrawOrigin = EnsureAddressTruncated; + type AddressMapping = HashedAddressMapping; + type Currency = Balances; + type RuntimeEvent = RuntimeEvent; + type PrecompilesType = (); + type PrecompilesValue = (); + type ChainId = ChainId; + type BlockGasLimit = BlockGasLimit; + type Runner = pallet_evm::runner::stack::Runner; + type OnChargeTransaction = (); + type OnCreate = (); + type FindAuthor = (); + type Timestamp = Timestamp; + type WeightInfo = (); + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; +} + +// parameter_types! { +// pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; +// } + +impl pallet_ethereum::Config for Test { + type RuntimeEvent = RuntimeEvent; + type StateRoot = (); + type PostLogContent = (); + type ExtraDataLength = ConstU32<30>; +} + // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { RuntimeGenesisConfig::default().build_storage().unwrap().into() diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index b58f0105..fe16723f 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -11,7 +11,8 @@ type AccountId = ::AccountId; type BaseURI = crate::BaseURI; const ALICE: AccountId = 0x1234; - +const BOB: AccountId = 0x2234; +use super::*; #[test] fn base_uri_unexistent_collection_is_none() { new_test_ext().execute_with(|| { @@ -120,6 +121,191 @@ fn test_is_collection_address_invalid() { assert!(!is_collection_address(invalid_address)); } +mod transfer_from { + use frame_support::assert_ok; + use sp_core::U256; + + use super::*; + + #[test] + fn owner_of_unexistent_asset_is_default_one() { + todo!(); + } + + #[test] + // fn sender_is_not_current_owner_should_fail() { + // new_test_ext().execute_with(|| { + // assert_ok!(LivingAssetsModule::transfer_from( + // RuntimeOrigin::signed(ALICE), + // ALICE, + // ALICE, + // U256::zero(), + // )); + // }); + // } + #[test] + fn sender_is_not_current_owner_should_fail() { + // impl_precompile_mock_simple!( + // Mock, + // // owner_of result + // Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), + // // transfer_from result + // Ok(()) + // ); + + // // test data + // let from = H160::repeat_byte(0xAA); + // let to = H160::repeat_byte(0xBB); + // let asset_id = 4; + // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + // .write(Address(from)) + // .write(Address(to)) + // .write(U256::from(asset_id)) + // .build(); + + // let mut handle = create_mock_handle_from_input(input_data); + // handle.code_address = contract_address.unwrap(); + // let result = Mock::execute(&mut handle); + // assert!(result.is_err()); + // assert_eq!(result.unwrap_err(), revert("sender must be the current owner"),); + } + + // #[test] + // fn receiver_is_the_current_owner_should_fail() { + // impl_precompile_mock_simple!( + // Mock, + // // owner_of result + // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // // transfer_from result + // Ok(()) + // ); + + // // test data + // let from = H160::repeat_byte(0xAA); + // let to = H160::repeat_byte(0xAA); + // let asset_id = 4; + // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + // .write(Address(from)) + // .write(Address(to)) + // .write(U256::from(asset_id)) + // .build(); + + // let mut handle = create_mock_handle_from_input(input_data); + // handle.code_address = contract_address.unwrap(); + // let result = Mock::execute(&mut handle); + // assert!(result.is_err()); + // assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same")); + // } + + // #[test] + // fn receiver_is_the_zero_address_should_fail() { + // impl_precompile_mock_simple!( + // Mock, + // // owner_of result + // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // // transfer_from result + // Ok(()) + // ); + + // // test data + // let from = H160::repeat_byte(0xAA); + // let to = H160::repeat_byte(0x0); + // let asset_id = 4; + // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + // .write(Address(from)) + // .write(Address(to)) + // .write(U256::from(asset_id)) + // .build(); + + // let mut handle = create_mock_handle_from_input(input_data); + // handle.code_address = contract_address.unwrap(); + // let result = Mock::execute(&mut handle); + // assert!(result.is_err()); + // assert_eq!(result.unwrap_err(), revert("receiver cannot be zero address")); + // } + + // #[test] + // fn send_value_as_money_should_fail() { + // impl_precompile_mock_simple!( + // Mock, + // // owner_of result + // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // // transfer_from result + // Ok(()) + // ); + + // // test data + // let from = H160::repeat_byte(0xAA); + // let to = H160::repeat_byte(0x0); + // let asset_id = 4; + // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + // .write(Address(from)) + // .write(Address(to)) + // .write(U256::from(asset_id)) + // .build(); + + // let mut handle = create_mock_handle(input_data, 0, 1, H160::zero()); + // handle.code_address = contract_address.unwrap(); + // let result = Mock::execute(&mut handle); + // assert!(result.is_err()); + // assert_eq!(result.unwrap_err(), revert("function is not payable")); + // } + + #[test] + fn sucessful_transfer_should_work() { + let asset_id = U256::zero(); + + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + assert_ok!(LivingAssetsModule::transfer_from( + RuntimeOrigin::signed(ALICE), + ALICE, + BOB, + asset_id, + )); + assert_eq!(Asset::::get(asset_id).unwrap(), BOB); + System::assert_last_event(Event::AssetTransferred { asset_id, receiver: BOB }.into()); + }); + } + // #[test] + // fn unsucessful_transfer_should_fail() { + // impl_precompile_mock_simple!( + // Mock, + // // owner_of result + // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), + // // transfer_from result + // Err("this is an error") + // ); + + // // test data + // let from = H160::repeat_byte(0xAA); + // let to = H160::repeat_byte(0xBB); + // let asset_id = 4; + // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + // .write(Address(from)) + // .write(Address(to)) + // .write(U256::from(asset_id)) + // .build(); + + // let mut handle = create_mock_handle_from_input(input_data); + // handle.code_address = contract_address.unwrap(); + // let result = Mock::execute(&mut handle); + // assert!(result.is_err()); + // assert_eq!(result.unwrap_err(), revert("this is an error")); + // } +} + mod traits { use super::*; use crate::{ From a1ec297d1b8e0852b69d599a2d4ec48b5a1fafff Mon Sep 17 00:00:00 2001 From: magecnion Date: Mon, 21 Aug 2023 18:55:55 +0200 Subject: [PATCH 099/114] add check to transfer_from precompile caller must be the owner --- precompile/erc721/src/lib.rs | 22 ++---------- precompile/erc721/src/tests.rs | 64 +++++++++++++++++++++++++++++++--- runtime/src/lib.rs | 2 +- 3 files changed, 62 insertions(+), 26 deletions(-) diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index f5805bfc..a70eae71 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -84,8 +84,10 @@ where Ok(value) => H160::from_slice(&value.as_slice()[12..32]), Err(_) => return Err(revert("error getting owner")), }; + let caller = handle.context().caller; // checks + ensure!(owner == caller, revert("caller must be the current owner")); ensure!(owner == from, revert("sender must be the current owner")); ensure!(from != to, revert("sender and receiver cannot be the same")); ensure!(to != H160::zero(), revert("receiver cannot be zero address")); @@ -108,25 +110,5 @@ where } } -// impl -// Erc721Precompile -// where -// AddressMapping: pallet_evm::AddressMapping, -// AccountId: Encode + Debug, -// AssetManager: Erc721, -// { -// fn owner_of(asset_id: U256, code_address: H160) -> Result { -// // collection id is encoded into the contract address -// let collection_id = match address_to_collection_id(code_address) { -// Ok(collection_id) => collection_id, -// Err(_) => return Err(revert("invalid collection address")), -// }; -// match AssetManager::owner_of(collection_id, asset_id) { -// Ok(owner) => Ok(owner), -// Err(err) => Err(revert(err)), -// } -// } -// } - #[cfg(test)] mod tests; diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 60f15e3e..4e8d2a40 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -89,6 +89,35 @@ mod transfer_from { use super::*; + #[test] + fn caller_is_not_current_owner_should_fail() { + impl_precompile_mock_simple!( + Mock, + // owner_of result + Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), + // transfer_from result + Ok(()) + ); + + // test data + let from = H160::repeat_byte(0xAA); + let to = H160::repeat_byte(0xBB); + let asset_id = 4; + let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); + + let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) + .write(Address(from)) + .write(Address(to)) + .write(U256::from(asset_id)) + .build(); + + let mut handle = create_mock_handle(input_data, 0, 0, H160::zero()); + handle.code_address = contract_address.unwrap(); + let result = Mock::execute(&mut handle); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), revert("caller must be the current owner"),); + } + #[test] fn sender_is_not_current_owner_should_fail() { impl_precompile_mock_simple!( @@ -111,7 +140,12 @@ mod transfer_from { .write(U256::from(asset_id)) .build(); - let mut handle = create_mock_handle_from_input(input_data); + let mut handle = create_mock_handle( + input_data, + 0, + 0, + H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap(), + ); handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); @@ -140,7 +174,12 @@ mod transfer_from { .write(U256::from(asset_id)) .build(); - let mut handle = create_mock_handle_from_input(input_data); + let mut handle = create_mock_handle( + input_data, + 0, + 0, + H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), + ); handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); @@ -169,7 +208,12 @@ mod transfer_from { .write(U256::from(asset_id)) .build(); - let mut handle = create_mock_handle_from_input(input_data); + let mut handle = create_mock_handle( + input_data, + 0, + 0, + H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), + ); handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); @@ -227,7 +271,12 @@ mod transfer_from { .write(U256::from(asset_id)) .build(); - let mut handle = create_mock_handle_from_input(input_data); + let mut handle = create_mock_handle( + input_data, + 0, + 0, + H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), + ); handle.code_address = contract_address.unwrap(); assert_ok!(Mock::execute(&mut handle)); } @@ -253,7 +302,12 @@ mod transfer_from { .write(U256::from(asset_id)) .build(); - let mut handle = create_mock_handle_from_input(input_data); + let mut handle = create_mock_handle( + input_data, + 0, + 0, + H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), + ); handle.code_address = contract_address.unwrap(); let result = Mock::execute(&mut handle); assert!(result.is_err()); diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index ce63f21e..b6e8d5d6 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -37,7 +37,7 @@ use sp_version::RuntimeVersion; use frame_support::{ construct_runtime, dispatch::DispatchClass, - parameter_types, + parameter_types traits::{ ConstBool, ConstU32, ConstU64, ConstU8, Currency, EitherOfDiverse, Everything, FindAuthor, Hooks, Imbalance, OnUnbalanced, From 15d48b15fedaa36f7b0433fc741006f7d2858c74 Mon Sep 17 00:00:00 2001 From: magecnion Date: Mon, 21 Aug 2023 18:56:29 +0200 Subject: [PATCH 100/114] add transfer_from trait impl to pallet --- pallets/living-assets-ownership/src/lib.rs | 52 +-- pallets/living-assets-ownership/src/mock.rs | 106 +++--- pallets/living-assets-ownership/src/tests.rs | 325 +++++++------------ 3 files changed, 186 insertions(+), 297 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index c3ac13f3..90a38c63 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -17,14 +17,11 @@ pub mod pallet { use super::*; use frame_support::{ pallet_prelude::{OptionQuery, ValueQuery, *}, - storage::types::QueryKindTrait, BoundedVec, }; use frame_system::pallet_prelude::*; - use pallet_evm::{pallet, AddressMapping}; - // use pallet_evm::AddressMapping; + use pallet_evm::AddressMapping; use sp_core::{H160, U256}; - // use sp_runtime::{traits::AccountIdConversion, AccountId32}; /// Collection id type pub type CollectionId = u64; @@ -95,7 +92,14 @@ pub mod pallet { CollectionIdOverflow, /// Unexistent collection UnexistentCollection, - // SenderIsNotTheCurrentOwner, + // SenderNotOwner, + SenderNotOwner, + // UnexistenAsset, + UnexistenAsset, + // SameSenderReceiver, + SameSenderReceiver, + // ReceiverIsZeroAddress, + ReceiverIsZeroAddress, } impl AsRef<[u8]> for Error { @@ -104,6 +108,10 @@ pub mod pallet { Error::__Ignore(_, _) => b"__Ignore", Error::CollectionIdOverflow => b"CollectionIdOverflow", Error::UnexistentCollection => b"UnexistentCollection", + Error::SenderNotOwner => b"SenderNotOwner", + Error::UnexistenAsset => b"UnexistenAsset", + Error::SameSenderReceiver => b"SameSenderReceiver", + Error::ReceiverIsZeroAddress => b"ReceiverIsZeroAddress", } } } @@ -123,27 +131,6 @@ pub mod pallet { Err(err) => Err(err.into()), } } - - #[pallet::call_index(1)] - #[pallet::weight(10_000 + T::DbWeight::get().writes(1).ref_time())] // TODO set proper weight - pub fn transfer_from( - origin: OriginFor, - from: T::AccountId, - to: T::AccountId, - asset_id: U256, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - // checks - // ensure!(owner == from, revert("sender must be the current owner")); - // ensure!(from != to, revert("sender and receiver cannot be the same")); - // ensure!(to != H160::zero(), revert("receiver cannot be zero address")); - - Asset::::set(asset_id, Some(to.clone())); - Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to }); - - Ok(()) - } } /// Errors that can occur when managing collections. @@ -199,6 +186,19 @@ pub mod pallet { to: T::AccountId, asset_id: U256, ) -> Result<(), Self::Error> { + // let who = ensure_signed(origin)?; // TODO check this cannot be called from pallet + + CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; + ensure!(asset::(asset_id) == from, Error::SenderNotOwner); + ensure!(from != to, Error::SameSenderReceiver); + ensure!( + to != T::AddressMapping::into_account_id(H160::zero()), + Error::ReceiverIsZeroAddress + ); + + Asset::::set(asset_id, Some(to.clone())); + Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to }); + Ok(()) } } diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index 0633601c..93f6fa58 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -1,13 +1,18 @@ use crate as pallet_livingassets_ownership; use frame_support::parameter_types; -use frame_support::traits::{ConstU16, ConstU64}; +use frame_support::traits::{ConstU16, ConstU64, FindAuthor}; use frame_support::weights::Weight; -use pallet_evm::{AddressMapping, EnsureAddressTruncated, FeeCalculator}; +use pallet_evm::{ + runner, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, FixedGasWeightMapping, + IdentityAddressMapping, SubstrateBlockHashMapping, +}; use sp_core::{ConstU32, H160, H256, U256}; +use sp_runtime::ConsensusEngineId; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, - AccountId32, BuildStorage, + BuildStorage, }; +use sp_std::{boxed::Box, prelude::*, str::FromStr}; type Block = frame_system::mocking::MockBlock; type Nonce = u32; @@ -20,6 +25,7 @@ frame_support::construct_runtime!( LivingAssetsModule: pallet_livingassets_ownership, Balances: pallet_balances, Timestamp: pallet_timestamp, + EVM: pallet_evm, } ); @@ -34,7 +40,7 @@ impl frame_system::Config for Test { type Hash = H256; type Nonce = Nonce; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = H160; type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; @@ -55,29 +61,27 @@ impl pallet_livingassets_ownership::Config for Test { } parameter_types! { - // For weight estimation, we assume that the most locks on an individual account will be 50. - // This number may need to be adjusted in the future if this assumption no longer holds true. - pub const MaxLocks: u32 = 50; - pub const ExistentialDeposit: u64 = 500; + pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Config for Test { - type MaxLocks = MaxLocks; - type Balance = u64; type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Balance = u64; type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; - type RuntimeHoldReason = (); - type WeightInfo = (); - type MaxReserves = (); type ReserveIdentifier = (); type FreezeIdentifier = (); + type MaxLocks = (); + type MaxReserves = (); type MaxHolds = (); type MaxFreezes = (); + type RuntimeHoldReason = (); } + parameter_types! { - pub const MinimumPeriod: u64 = 6000 / 2; + pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Test { @@ -86,74 +90,52 @@ impl pallet_timestamp::Config for Test { type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } + pub struct FixedGasPrice; impl FeeCalculator for FixedGasPrice { fn min_gas_price() -> (U256, Weight) { - (1.into(), Weight::zero()) + // Return some meaningful gas price and weight + (1_000_000_000u128.into(), Weight::from_parts(7u64, 0)) } } -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; -parameter_types! { - pub const TransactionByteFee: u64 = 1; - pub const ChainId: u64 = 42; - pub const BlockGasLimit: U256 = U256::MAX; - pub WeightPerGas: Weight = Weight::from_parts(1, 0); - pub GasLimitPovSizeRatio: u64 = { - let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64(); - block_gas_limit.saturating_div(MAX_POV_SIZE) - }; +pub struct FindAuthorTruncated; +impl FindAuthor for FindAuthorTruncated { + fn find_author<'a, I>(_digests: I) -> Option + where + I: 'a + IntoIterator, + { + Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) + } } +const BLOCK_GAS_LIMIT: u64 = 150_000_000; +const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; -pub struct HashedAddressMapping; - -impl AddressMapping for HashedAddressMapping { - fn into_account_id(address: H160) -> AccountId32 { - let mut data = [0u8; 32]; - data[0..20].copy_from_slice(&address[..]); - AccountId32::from(Into::<[u8; 32]>::into(data)) - } +parameter_types! { + pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); + pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); + pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); } -// pub struct FindAuthorTruncated; -// impl FindAuthor for FindAuthorTruncated { -// fn find_author<'a, I>(_digests: I) -> Option -// where -// I: 'a + IntoIterator, -// { -// Some(address_build(0).address) -// } -// } impl pallet_evm::Config for Test { type FeeCalculator = FixedGasPrice; - type GasWeightMapping = pallet_evm::FixedGasWeightMapping; + type GasWeightMapping = FixedGasWeightMapping; type WeightPerGas = WeightPerGas; - type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping; - type CallOrigin = EnsureAddressTruncated; - type WithdrawOrigin = EnsureAddressTruncated; - type AddressMapping = HashedAddressMapping; + type BlockHashMapping = SubstrateBlockHashMapping; + type CallOrigin = EnsureAddressRoot; + type WithdrawOrigin = EnsureAddressNever; + type AddressMapping = IdentityAddressMapping; type Currency = Balances; type RuntimeEvent = RuntimeEvent; type PrecompilesType = (); type PrecompilesValue = (); - type ChainId = ChainId; + type ChainId = (); type BlockGasLimit = BlockGasLimit; - type Runner = pallet_evm::runner::stack::Runner; + type Runner = runner::stack::Runner; type OnChargeTransaction = (); type OnCreate = (); - type FindAuthor = (); + type FindAuthor = FindAuthorTruncated; + type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type Timestamp = Timestamp; type WeightInfo = (); - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; -} - -// parameter_types! { -// pub const PostBlockAndTxnHashes: PostLogContent = PostLogContent::BlockAndTxnHashes; -// } - -impl pallet_ethereum::Config for Test { - type RuntimeEvent = RuntimeEvent; - type StateRoot = (); - type PostLogContent = (); - type ExtraDataLength = ConstU32<30>; } // Build genesis storage according to the mock runtime. diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index fe16723f..7c2830e9 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -7,11 +7,10 @@ use crate::{ use frame_support::assert_ok; use sp_core::H160; -type AccountId = ::AccountId; type BaseURI = crate::BaseURI; -const ALICE: AccountId = 0x1234; -const BOB: AccountId = 0x2234; +const ALICE: &'static str = "0xffffffffffffffffffffffff0000000000000001"; +const BOB: &'static str = "0xffffffffffffffffffffffff0000000000000002"; use super::*; #[test] fn base_uri_unexistent_collection_is_none() { @@ -33,7 +32,7 @@ fn create_new_collection_should_create_sequential_collections() { for i in 0..3 { // Create the collection assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), base_uri.clone() )); @@ -49,7 +48,7 @@ fn should_set_base_uri_when_creating_new_collection() { new_test_ext().execute_with(|| { assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), base_uri.clone() )); assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); @@ -63,25 +62,37 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { System::set_block_number(1); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), BaseURI::default() )); - System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); + System::assert_last_event( + Event::CollectionCreated { collection_id: 0, who: H160::from_str(ALICE).unwrap() } + .into(), + ); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), BaseURI::default() )); - System::assert_last_event(Event::CollectionCreated { collection_id: 1, who: ALICE }.into()); + System::assert_last_event( + Event::CollectionCreated { collection_id: 1, who: H160::from_str(ALICE).unwrap() } + .into(), + ); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), BaseURI::default() )); - System::assert_last_event(Event::CollectionCreated { collection_id: 2, who: ALICE }.into()); + System::assert_last_event( + Event::CollectionCreated { collection_id: 2, who: H160::from_str(ALICE).unwrap() } + .into(), + ); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(ALICE), + RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), BaseURI::default() )); - System::assert_last_event(Event::CollectionCreated { collection_id: 3, who: ALICE }.into()); + System::assert_last_event( + Event::CollectionCreated { collection_id: 3, who: H160::from_str(ALICE).unwrap() } + .into(), + ); }); } @@ -121,198 +132,14 @@ fn test_is_collection_address_invalid() { assert!(!is_collection_address(invalid_address)); } -mod transfer_from { - use frame_support::assert_ok; - use sp_core::U256; - - use super::*; - - #[test] - fn owner_of_unexistent_asset_is_default_one() { - todo!(); - } - - #[test] - // fn sender_is_not_current_owner_should_fail() { - // new_test_ext().execute_with(|| { - // assert_ok!(LivingAssetsModule::transfer_from( - // RuntimeOrigin::signed(ALICE), - // ALICE, - // ALICE, - // U256::zero(), - // )); - // }); - // } - #[test] - fn sender_is_not_current_owner_should_fail() { - // impl_precompile_mock_simple!( - // Mock, - // // owner_of result - // Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), - // // transfer_from result - // Ok(()) - // ); - - // // test data - // let from = H160::repeat_byte(0xAA); - // let to = H160::repeat_byte(0xBB); - // let asset_id = 4; - // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - // .write(Address(from)) - // .write(Address(to)) - // .write(U256::from(asset_id)) - // .build(); - - // let mut handle = create_mock_handle_from_input(input_data); - // handle.code_address = contract_address.unwrap(); - // let result = Mock::execute(&mut handle); - // assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), revert("sender must be the current owner"),); - } - - // #[test] - // fn receiver_is_the_current_owner_should_fail() { - // impl_precompile_mock_simple!( - // Mock, - // // owner_of result - // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // // transfer_from result - // Ok(()) - // ); - - // // test data - // let from = H160::repeat_byte(0xAA); - // let to = H160::repeat_byte(0xAA); - // let asset_id = 4; - // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - // .write(Address(from)) - // .write(Address(to)) - // .write(U256::from(asset_id)) - // .build(); - - // let mut handle = create_mock_handle_from_input(input_data); - // handle.code_address = contract_address.unwrap(); - // let result = Mock::execute(&mut handle); - // assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same")); - // } - - // #[test] - // fn receiver_is_the_zero_address_should_fail() { - // impl_precompile_mock_simple!( - // Mock, - // // owner_of result - // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // // transfer_from result - // Ok(()) - // ); - - // // test data - // let from = H160::repeat_byte(0xAA); - // let to = H160::repeat_byte(0x0); - // let asset_id = 4; - // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - // .write(Address(from)) - // .write(Address(to)) - // .write(U256::from(asset_id)) - // .build(); - - // let mut handle = create_mock_handle_from_input(input_data); - // handle.code_address = contract_address.unwrap(); - // let result = Mock::execute(&mut handle); - // assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), revert("receiver cannot be zero address")); - // } - - // #[test] - // fn send_value_as_money_should_fail() { - // impl_precompile_mock_simple!( - // Mock, - // // owner_of result - // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // // transfer_from result - // Ok(()) - // ); - - // // test data - // let from = H160::repeat_byte(0xAA); - // let to = H160::repeat_byte(0x0); - // let asset_id = 4; - // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - // .write(Address(from)) - // .write(Address(to)) - // .write(U256::from(asset_id)) - // .build(); - - // let mut handle = create_mock_handle(input_data, 0, 1, H160::zero()); - // handle.code_address = contract_address.unwrap(); - // let result = Mock::execute(&mut handle); - // assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), revert("function is not payable")); - // } - - #[test] - fn sucessful_transfer_should_work() { - let asset_id = U256::zero(); - - new_test_ext().execute_with(|| { - System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); - assert_ok!(LivingAssetsModule::transfer_from( - RuntimeOrigin::signed(ALICE), - ALICE, - BOB, - asset_id, - )); - assert_eq!(Asset::::get(asset_id).unwrap(), BOB); - System::assert_last_event(Event::AssetTransferred { asset_id, receiver: BOB }.into()); - }); - } - // #[test] - // fn unsucessful_transfer_should_fail() { - // impl_precompile_mock_simple!( - // Mock, - // // owner_of result - // Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // // transfer_from result - // Err("this is an error") - // ); - - // // test data - // let from = H160::repeat_byte(0xAA); - // let to = H160::repeat_byte(0xBB); - // let asset_id = 4; - // let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - // let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - // .write(Address(from)) - // .write(Address(to)) - // .write(U256::from(asset_id)) - // .build(); - - // let mut handle = create_mock_handle_from_input(input_data); - // handle.code_address = contract_address.unwrap(); - // let result = Mock::execute(&mut handle); - // assert!(result.is_err()); - // assert_eq!(result.unwrap_err(), revert("this is an error")); - // } -} - mod traits { use super::*; use crate::{ traits::{CollectionManager, Erc721}, Error, Event, }; - use frame_support::{assert_err, assert_ok}; + use frame_support::{assert_err, assert_noop, assert_ok}; + use sp_core::U256; #[test] fn base_uri_of_unexistent_collection_is_none() { @@ -329,11 +156,12 @@ mod traits { System::set_block_number(1); assert_ok!(::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default(), )); System::assert_last_event( - Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), + Event::CollectionCreated { collection_id: 0, who: H160::from_str(ALICE).unwrap() } + .into(), ); }); } @@ -343,7 +171,7 @@ mod traits { new_test_ext().execute_with(|| { assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -351,7 +179,7 @@ mod traits { ); assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -359,7 +187,7 @@ mod traits { ); assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -367,7 +195,7 @@ mod traits { ); assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -375,7 +203,7 @@ mod traits { ); assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -383,7 +211,7 @@ mod traits { ); assert_eq!( ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default() ) .unwrap(), @@ -398,7 +226,7 @@ mod traits { new_test_ext().execute_with(|| { assert_ok!(::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), base_uri.clone() )); assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); @@ -417,7 +245,7 @@ mod traits { fn erc721_owner_of_asset_of_collection() { new_test_ext().execute_with(|| { let collection_id = ::create_collection( - ALICE, + H160::from_str(ALICE).unwrap(), BaseURI::default(), ) .unwrap(); @@ -427,4 +255,83 @@ mod traits { ); }); } + + #[test] + fn sender_is_not_current_owner_should_fail() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000006").unwrap(); + let receiver = H160::from_str(BOB).unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + CollectionBaseURI::::insert(1, BaseURI::default()); + assert_noop!( + ::transfer_from(1, sender, receiver, asset_id,), + Error::::SenderNotOwner + ); + }); + } + + #[test] + fn same_sender_and_receiver_should_fail() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + CollectionBaseURI::::insert(1, BaseURI::default()); + assert_noop!( + ::transfer_from(1, sender, sender, asset_id,), + Error::::SameSenderReceiver + ); + }); + } + + #[test] + fn receiver_is_the_zero_address_should_fail() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); + let receiver = H160::from_str("0000000000000000000000000000000000000000").unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + CollectionBaseURI::::insert(1, BaseURI::default()); + assert_noop!( + ::transfer_from(1, sender, receiver, asset_id,), + Error::::ReceiverIsZeroAddress + ); + }); + } + + #[test] + fn unexistent_collection_when_transfer_from_should_fail() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); + let receiver = H160::from_str(BOB).unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + assert_noop!( + ::transfer_from(1, sender, receiver, asset_id,), + Error::::UnexistentCollection + ); + }); + } + + #[test] + fn sucessful_transfer_from_trait_should_work() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); + let receiver = H160::from_str(BOB).unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + CollectionBaseURI::::insert(1, BaseURI::default()); + assert_ok!(::transfer_from( + 1, sender, receiver, asset_id, + )); + assert_eq!(Asset::::get(asset_id).unwrap(), receiver); + System::assert_last_event(Event::AssetTransferred { asset_id, receiver }.into()); + }); + } } From 6fcdfb4e2166d80e2eb2d3c6b65001a311af094f Mon Sep 17 00:00:00 2001 From: magecnion Date: Mon, 21 Aug 2023 18:56:45 +0200 Subject: [PATCH 101/114] fmt --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index b6e8d5d6..ce63f21e 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -37,7 +37,7 @@ use sp_version::RuntimeVersion; use frame_support::{ construct_runtime, dispatch::DispatchClass, - parameter_types + parameter_types, traits::{ ConstBool, ConstU32, ConstU64, ConstU8, Currency, EitherOfDiverse, Everything, FindAuthor, Hooks, Imbalance, OnUnbalanced, From 200291f83cd48a18e34b8a0852e862d5211092dc Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 10:00:20 +0200 Subject: [PATCH 102/114] owner_of trait return current owner --- .../living-assets-ownership/src/functions.rs | 30 +++++++++++++++++++ pallets/living-assets-ownership/src/lib.rs | 7 ++--- pallets/living-assets-ownership/src/tests.rs | 4 ++- pallets/living-assets-ownership/src/traits.rs | 2 +- precompile/living-assets/src/lib.rs | 2 +- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index c9c8f39e..9f37c25d 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -43,4 +43,34 @@ mod tests { let expected_address = H160::from_low_u64_be(5); assert_eq!(convert_asset_id_to_owner(value), expected_address); } + + #[test] + fn check_two_assets_same_owner() { + // create two different assets + let asset1 = U256::from( + hex::decode("01C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), + ); + let asset2 = U256::from( + hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), + ); + assert_ne!(asset1, asset2); + + // check asset in decimal format + assert_eq!( + U256::from_str_radix("01C0F0f4ab324C46e55D02D0033343B4Be8A55532d", 16).unwrap(), + U256::from_dec_str("2563001357829637001682277476112176020532353127213").unwrap() + ); + assert_eq!( + U256::from_str_radix("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d", 16).unwrap(), + U256::from_dec_str("5486004632491442838089647141544742059844218213165").unwrap() + ); + + let mut owner = [0u8; 20]; + owner.copy_from_slice( + hex::decode("C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), + ); + let expected_address = H160::from(owner); + assert_eq!(convert_asset_id_to_owner(asset1), expected_address); + assert_eq!(convert_asset_id_to_owner(asset2), expected_address); + } } diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 90a38c63..ceee4580 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -174,10 +174,9 @@ pub mod pallet { type AccountId = T::AccountId; fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { - match CollectionBaseURI::::get(collection_id) { - Some(_) => Ok(convert_asset_id_to_owner(asset_id)), - None => Err(Error::UnexistentCollection), - } + CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; + let owner = H160::from_slice(asset::(asset_id).encode().as_slice()); + Ok(owner.into()) } fn transfer_from( diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 7c2830e9..cdde190e 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -325,12 +325,14 @@ mod traits { let receiver = H160::from_str(BOB).unwrap(); new_test_ext().execute_with(|| { System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); + assert!(Asset::::get(asset_id).is_none()); + assert_eq!(::owner_of(1, asset_id).unwrap(), sender); assert_ok!(::transfer_from( 1, sender, receiver, asset_id, )); assert_eq!(Asset::::get(asset_id).unwrap(), receiver); + assert_eq!(::owner_of(1, asset_id).unwrap(), receiver); System::assert_last_event(Event::AssetTransferred { asset_id, receiver }.into()); }); } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 7ef88e53..05a515da 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -1,4 +1,4 @@ -use crate::{BaseURI, CollectionId}; +use crate::CollectionId; use sp_core::{H160, U256}; use sp_std::vec::Vec; diff --git a/precompile/living-assets/src/lib.rs b/precompile/living-assets/src/lib.rs index 817bd51c..38af986c 100644 --- a/precompile/living-assets/src/lib.rs +++ b/precompile/living-assets/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use pallet_living_assets_ownership::{ - collection_id_to_address, traits::CollectionManager, BaseURI, CollectionId, + collection_id_to_address, traits::CollectionManager, CollectionId, }; use parity_scale_codec::Encode; use precompile_utils::{ From 0e384200ed95dd50459bde85a431ef5ce75d8863 Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 10:31:15 +0200 Subject: [PATCH 103/114] add remix test --- pallets/living-assets-ownership/src/tests.rs | 7 +++++-- precompile/erc721/contracts/IERC721.sol | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index cdde190e..e5d0c470 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -320,8 +320,11 @@ mod traits { #[test] fn sucessful_transfer_from_trait_should_work() { - let asset_id = U256::from(5); - let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); + // let asset_id = U256::from(5); + let asset_id = U256::from( + hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), + ); + let sender = H160::from_str("C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap(); let receiver = H160::from_str(BOB).unwrap(); new_test_ext().execute_with(|| { System::set_block_number(1); diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol index 3a54aca5..65395785 100644 --- a/precompile/erc721/contracts/IERC721.sol +++ b/precompile/erc721/contracts/IERC721.sol @@ -10,5 +10,5 @@ interface IERC721 { function ownerOf(uint256 _tokenId) external view returns (address); - function transferFrom(address _from, address _to, uint256 _tokenId) external payable; + function transferFrom(address _from, address _to, uint256 _tokenId) external; } From 68aee06933ef570c1a447da64cb97d407b81cfa5 Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 13:19:14 +0200 Subject: [PATCH 104/114] use H160 instead of T::AcountId --- pallets/living-assets-ownership/src/lib.rs | 29 ++++++------------- pallets/living-assets-ownership/src/tests.rs | 1 - pallets/living-assets-ownership/src/traits.rs | 4 +-- precompile/erc721/src/lib.rs | 7 +---- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index ceee4580..f2b540cb 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -20,7 +20,6 @@ pub mod pallet { BoundedVec, }; use frame_system::pallet_prelude::*; - use pallet_evm::AddressMapping; use sp_core::{H160, U256}; /// Collection id type @@ -62,14 +61,10 @@ pub mod pallet { StorageMap<_, Blake2_128Concat, CollectionId, BaseURI, OptionQuery>; #[pallet::storage] - pub(super) type Asset = - StorageMap<_, Blake2_128Concat, U256, T::AccountId, OptionQuery>; - - fn asset(key: U256) -> ::AccountId { - Asset::::get(key).unwrap_or_else(|| { - let owner = convert_asset_id_to_owner(key); - ::AddressMapping::into_account_id(owner) - }) + pub(super) type Asset = StorageMap<_, Blake2_128Concat, U256, H160, OptionQuery>; + + fn asset(key: U256) -> H160 { + Asset::::get(key).unwrap_or_else(|| convert_asset_id_to_owner(key)) } /// Pallet events @@ -81,7 +76,7 @@ pub mod pallet { CollectionCreated { collection_id: CollectionId, who: T::AccountId }, /// Asset transferred to `who` /// parameters. [asset_id_id, who] - AssetTransferred { asset_id: U256, receiver: T::AccountId }, + AssetTransferred { asset_id: U256, receiver: H160 }, } // Errors inform users that something went wrong. @@ -175,25 +170,19 @@ pub mod pallet { fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; - let owner = H160::from_slice(asset::(asset_id).encode().as_slice()); - Ok(owner.into()) + Ok(asset::(asset_id)) } fn transfer_from( collection_id: CollectionId, - from: T::AccountId, - to: T::AccountId, + from: H160, + to: H160, asset_id: U256, ) -> Result<(), Self::Error> { - // let who = ensure_signed(origin)?; // TODO check this cannot be called from pallet - CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; ensure!(asset::(asset_id) == from, Error::SenderNotOwner); ensure!(from != to, Error::SameSenderReceiver); - ensure!( - to != T::AddressMapping::into_account_id(H160::zero()), - Error::ReceiverIsZeroAddress - ); + ensure!(to != H160::zero(), Error::ReceiverIsZeroAddress); Asset::::set(asset_id, Some(to.clone())); Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to }); diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index e5d0c470..cbff7abd 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -320,7 +320,6 @@ mod traits { #[test] fn sucessful_transfer_from_trait_should_work() { - // let asset_id = U256::from(5); let asset_id = U256::from( hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), ); diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 05a515da..c97d4004 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -75,8 +75,8 @@ pub trait Erc721 { /// * `asset_id` - The unique identifier for the asset within the collection. fn transfer_from( collection_id: CollectionId, - from: Self::AccountId, - to: Self::AccountId, + from: H160, + to: H160, asset_id: U256, ) -> Result<(), Self::Error>; } diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index a70eae71..964d672f 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -98,12 +98,7 @@ where Err(_) => return Err(revert("invalid collection address")), }; - match AssetManager::transfer_from( - collection_id, - AddressMapping::into_account_id(from), - AddressMapping::into_account_id(to), - asset_id, - ) { + match AssetManager::transfer_from(collection_id, from, to, asset_id) { Ok(_) => Ok(succeed(vec![])), Err(err) => Err(revert(err)), } From 89ffbf91aa75183eb5e5994416368d5ddc4e1a2a Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 18:00:23 +0200 Subject: [PATCH 105/114] change requests --- pallets/living-assets-ownership/src/lib.rs | 39 +++++++------- pallets/living-assets-ownership/src/tests.rs | 10 ++-- pallets/living-assets-ownership/src/traits.rs | 1 - precompile/erc721/src/lib.rs | 51 +++++++++++-------- precompile/erc721/src/tests.rs | 4 +- runtime/src/precompiles/mock.rs | 2 +- runtime/src/precompiles/mod.rs | 5 +- 7 files changed, 56 insertions(+), 56 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 9eec0ad9..c3df4be1 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -64,7 +64,7 @@ pub mod pallet { #[pallet::storage] pub(super) type Asset = StorageMap<_, Blake2_128Concat, U256, H160, OptionQuery>; - fn asset(key: U256) -> H160 { + fn asset_owner(key: U256) -> H160 { Asset::::get(key).unwrap_or_else(|| convert_asset_id_to_owner(key)) } @@ -86,14 +86,14 @@ pub mod pallet { pub enum Error { /// Collection id overflow CollectionIdOverflow, - /// Unexistent collection - UnexistentCollection, - // SenderNotOwner, - SenderNotOwner, - // UnexistenAsset, - UnexistenAsset, - // SameSenderReceiver, - SameSenderReceiver, + /// Collection does not exist + CollectionDoesNotExist, + // NoPermission, + NoPermission, + // AssetDoesNotExist, + AssetDoesNotExist, + // CannotTransferSelf, + CannotTransferSelf, // ReceiverIsZeroAddress, ReceiverIsZeroAddress, } @@ -103,10 +103,10 @@ pub mod pallet { match self { Error::__Ignore(_, _) => b"__Ignore", Error::CollectionIdOverflow => b"CollectionIdOverflow", - Error::UnexistentCollection => b"UnexistentCollection", - Error::SenderNotOwner => b"SenderNotOwner", - Error::UnexistenAsset => b"UnexistenAsset", - Error::SameSenderReceiver => b"SameSenderReceiver", + Error::CollectionDoesNotExist => b"CollectionDoesNotExist", + Error::NoPermission => b"NoPermission", + Error::AssetDoesNotExist => b"AssetDoesNotExist", + Error::CannotTransferSelf => b"CannotTransferSelf", Error::ReceiverIsZeroAddress => b"ReceiverIsZeroAddress", } } @@ -167,11 +167,10 @@ pub mod pallet { impl traits::Erc721 for Pallet { type Error = Error; - type AccountId = T::AccountId; fn owner_of(collection_id: CollectionId, asset_id: U256) -> Result { - CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; - Ok(asset::(asset_id)) + Pallet::::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?; + Ok(asset_owner::(asset_id)) } fn transfer_from( @@ -180,9 +179,9 @@ pub mod pallet { to: H160, asset_id: U256, ) -> Result<(), Self::Error> { - CollectionBaseURI::::get(collection_id).ok_or(Error::UnexistentCollection)?; - ensure!(asset::(asset_id) == from, Error::SenderNotOwner); - ensure!(from != to, Error::SameSenderReceiver); + Pallet::::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?; + ensure!(asset_owner::(asset_id) == from, Error::NoPermission); + ensure!(from != to, Error::CannotTransferSelf); ensure!(to != H160::zero(), Error::ReceiverIsZeroAddress); Asset::::set(asset_id, Some(to.clone())); @@ -193,7 +192,7 @@ pub mod pallet { fn token_uri(collection_id: CollectionId, asset_id: U256) -> Result, Self::Error> { let base_uri = Pallet::::collection_base_uri(collection_id) - .ok_or(Error::UnexistentCollection)?; + .ok_or(Error::CollectionDoesNotExist)?; // concatenate base_uri with asset_id let mut token_uri = base_uri.to_vec(); diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 8df2215a..e20c7166 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -237,7 +237,7 @@ mod traits { fn owner_of_asset_of_unexistent_collection_should_error() { new_test_ext().execute_with(|| { let result = ::owner_of(0, 2.into()); - assert_err!(result, Error::UnexistentCollection); + assert_err!(result, Error::CollectionDoesNotExist); }); } @@ -267,7 +267,7 @@ mod traits { CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( ::transfer_from(1, sender, receiver, asset_id,), - Error::::SenderNotOwner + Error::::NoPermission ); }); } @@ -282,7 +282,7 @@ mod traits { CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( ::transfer_from(1, sender, sender, asset_id,), - Error::::SameSenderReceiver + Error::::CannotTransferSelf ); }); } @@ -313,7 +313,7 @@ mod traits { assert!(Asset::::get(asset_id).is_none()); assert_noop!( ::transfer_from(1, sender, receiver, asset_id,), - Error::::UnexistentCollection + Error::::CollectionDoesNotExist ); }); } @@ -343,7 +343,7 @@ mod traits { fn token_uri_of_unexistent_collection() { new_test_ext().execute_with(|| { let result = ::token_uri(0, 2.into()); - assert_err!(result, Error::UnexistentCollection); + assert_err!(result, Error::CollectionDoesNotExist); }); } diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index 13fe3711..dc8cf25d 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -52,7 +52,6 @@ pub trait CollectionManager { /// - `token_uri`: Retrieve the URI associated with a specific asset within a collection. pub trait Erc721 { type Error: AsRef<[u8]> + PartialEq; - type AccountId; /// Retrieves the owner of a specific asset within the specified collection. /// diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index f2f3a16c..93f52871 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -3,13 +3,17 @@ use fp_evm::{Precompile, PrecompileHandle, PrecompileOutput}; use frame_support::pallet_prelude::*; use pallet_living_assets_ownership::{address_to_collection_id, CollectionId}; use precompile_utils::{ - revert, succeed, Address, Bytes, EvmDataWriter, EvmResult, FunctionModifier, - PrecompileHandleExt, + keccak256, revert, succeed, Address, Bytes, EvmDataWriter, EvmResult, FunctionModifier, LogExt, + LogsBuilder, PrecompileHandleExt, }; -use sp_core::{H160, U256}; +use sp_core::{H160, H256, U256}; use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; +/// Solidity selector of the TransferFrom log, which is the Keccak of the Log signature. +pub const SELECTOR_LOG_TRANSFER_FROM: [u8; 32] = + keccak256!("TransferFrom(address,address,uint256)"); + #[precompile_utils_macro::generate_function_selector] #[derive(Debug, PartialEq)] pub enum Action { @@ -22,14 +26,11 @@ pub enum Action { } /// Wrapper for the precompile function. -pub struct Erc721Precompile( - PhantomData<(AssetManager, AddressMapping)>, -); +pub struct Erc721Precompile(PhantomData); -impl Precompile for Erc721Precompile +impl Precompile for Erc721Precompile where AssetManager: pallet_living_assets_ownership::traits::Erc721, - AddressMapping: pallet_evm::AddressMapping, { fn execute(handle: &mut impl PrecompileHandle) -> EvmResult { // collection id is encoded into the contract address @@ -47,15 +48,14 @@ where match selector { Action::TokenURI => Self::token_uri(collection_id, handle), Action::OwnerOf => Self::owner_of(collection_id, handle), - Action::TransferFrom => Self::transfer_from(handle), + Action::TransferFrom => Self::transfer_from(collection_id, handle), } } } -impl Erc721Precompile +impl Erc721Precompile where AssetManager: pallet_living_assets_ownership::traits::Erc721, - AddressMapping: pallet_evm::AddressMapping, { fn owner_of( collection_id: CollectionId, @@ -83,7 +83,10 @@ where Ok(succeed(EvmDataWriter::new().write(Bytes(uri)).build())) } - fn transfer_from(handle: &mut impl PrecompileHandle) -> EvmResult { + fn transfer_from( + collection_id: CollectionId, + handle: &mut impl PrecompileHandle, + ) -> EvmResult { // get input data let mut input = handle.read_input()?; input.expect_arguments(3)?; @@ -91,12 +94,6 @@ where let to: H160 = input.read::
()?.into(); let asset_id: U256 = input.read()?; - // collection id is encoded into the contract address - let collection_id = match address_to_collection_id(handle.code_address()) { - Ok(collection_id) => collection_id, - Err(_) => return Err(revert("invalid collection address")), - }; - // get current owner let result = Self::owner_of(collection_id, handle)?; let owner: H160 = match TryInto::>::try_into(result.output) { @@ -111,10 +108,20 @@ where ensure!(from != to, revert("sender and receiver cannot be the same")); ensure!(to != H160::zero(), revert("receiver cannot be zero address")); - match AssetManager::transfer_from(collection_id, from, to, asset_id) { - Ok(_) => Ok(succeed(vec![])), - Err(err) => Err(revert(err)), - } + AssetManager::transfer_from(collection_id, from, to, asset_id) + .map_err(|err| revert(err))?; + + LogsBuilder::new(handle.context().address) + .log4( + SELECTOR_LOG_TRANSFER_FROM, + from, + to, + H256::from_slice(asset_id.encode().as_slice()), + Vec::new(), + ) + .record(handle)?; + + Ok(succeed(vec![])) } } diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 3110d2f4..021e4d61 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -7,7 +7,6 @@ use precompile_utils::testing::create_mock_handle_from_input; use sp_core::{H160, U256}; type AccountId = H160; -type AddressMapping = pallet_evm::IdentityAddressMapping; #[test] fn check_selectors() { @@ -382,7 +381,6 @@ mod helpers { impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { type Error = &'static str; - type AccountId = AccountId; fn owner_of( collection_id: CollectionId, @@ -408,7 +406,7 @@ mod helpers { } } - type $name = Erc721Precompile; + type $name = Erc721Precompile; }; } diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs index d1a8951a..9c47fa33 100644 --- a/runtime/src/precompiles/mock.rs +++ b/runtime/src/precompiles/mock.rs @@ -38,7 +38,7 @@ impl frame_system::Config for Runtime { type RuntimeCall = RuntimeCall; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = H160; + type AccountId = u64; type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 6879212f..6563d5f5 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -35,10 +35,7 @@ type LivingAssetsPrecompile = CollectionManagerPrecompile< pallet_living_assets_ownership::Pallet, >; -type Erc721 = Erc721Precompile< - pallet_living_assets_ownership::Pallet, - pallet_evm::HashedAddressMapping, ->; +type Erc721 = Erc721Precompile>; impl PrecompileSet for FrontierPrecompiles where From 5fdd84b24a031dbeeb83407cdb2405dfd8930c84 Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 18:07:54 +0200 Subject: [PATCH 106/114] add eth event --- precompile/erc721/contracts/IERC721.sol | 2 ++ precompile/erc721/src/lib.rs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/precompile/erc721/contracts/IERC721.sol b/precompile/erc721/contracts/IERC721.sol index 65395785..5c4014cf 100644 --- a/precompile/erc721/contracts/IERC721.sol +++ b/precompile/erc721/contracts/IERC721.sol @@ -10,5 +10,7 @@ interface IERC721 { function ownerOf(uint256 _tokenId) external view returns (address); + event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); + function transferFrom(address _from, address _to, uint256 _tokenId) external; } diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index 93f52871..d4712e60 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -11,8 +11,7 @@ use sp_core::{H160, H256, U256}; use sp_std::{fmt::Debug, marker::PhantomData, vec, vec::Vec}; /// Solidity selector of the TransferFrom log, which is the Keccak of the Log signature. -pub const SELECTOR_LOG_TRANSFER_FROM: [u8; 32] = - keccak256!("TransferFrom(address,address,uint256)"); +pub const SELECTOR_LOG_TRANSFER_FROM: [u8; 32] = keccak256!("Transfer(address,address,uint256)"); #[precompile_utils_macro::generate_function_selector] #[derive(Debug, PartialEq)] From cf720b17b7c8ca4e6a4b424580a9d6358fcbbbd9 Mon Sep 17 00:00:00 2001 From: magecnion Date: Tue, 22 Aug 2023 18:12:52 +0200 Subject: [PATCH 107/114] from u64 to H160 --- runtime/src/precompiles/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/mock.rs b/runtime/src/precompiles/mock.rs index 9c47fa33..d1a8951a 100644 --- a/runtime/src/precompiles/mock.rs +++ b/runtime/src/precompiles/mock.rs @@ -38,7 +38,7 @@ impl frame_system::Config for Runtime { type RuntimeCall = RuntimeCall; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = H160; type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; From 61b9d840a1d92c614a2ff0451fa97df231fe12a0 Mon Sep 17 00:00:00 2001 From: magecnion Date: Wed, 23 Aug 2023 10:49:20 +0200 Subject: [PATCH 108/114] wip --- pallets/living-assets-ownership/src/lib.rs | 2 + pallets/living-assets-ownership/src/tests.rs | 38 ++++++++++++++++--- pallets/living-assets-ownership/src/traits.rs | 2 + 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index c3df4be1..e4fec420 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -174,12 +174,14 @@ pub mod pallet { } fn transfer_from( + origin: H160, collection_id: CollectionId, from: H160, to: H160, asset_id: U256, ) -> Result<(), Self::Error> { Pallet::::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?; + ensure!(origin == from, Error::NoPermission); ensure!(asset_owner::(asset_id) == from, Error::NoPermission); ensure!(from != to, Error::CannotTransferSelf); ensure!(to != H160::zero(), Error::ReceiverIsZeroAddress); diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index e20c7166..555b602b 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -256,6 +256,28 @@ mod traits { }); } + #[test] + fn caller_is_not_current_owner_should_fail() { + let asset_id = U256::from(5); + let sender = H160::from_str("0000000000000000000000000000000000000006").unwrap(); + let receiver = H160::from_str(BOB).unwrap(); + new_test_ext().execute_with(|| { + System::set_block_number(1); + assert!(Asset::::get(asset_id).is_none()); + CollectionBaseURI::::insert(1, BaseURI::default()); + assert_noop!( + ::transfer_from( + H160::from_str(ALICE).unwrap(), + 1, + sender, + receiver, + asset_id, + ), + Error::::NoPermission + ); + }); + } + #[test] fn sender_is_not_current_owner_should_fail() { let asset_id = U256::from(5); @@ -266,7 +288,9 @@ mod traits { assert!(Asset::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( - ::transfer_from(1, sender, receiver, asset_id,), + ::transfer_from( + sender, 1, sender, receiver, asset_id, + ), Error::::NoPermission ); }); @@ -281,7 +305,7 @@ mod traits { assert!(Asset::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( - ::transfer_from(1, sender, sender, asset_id,), + ::transfer_from(sender, 1, sender, sender, asset_id,), Error::::CannotTransferSelf ); }); @@ -297,7 +321,9 @@ mod traits { assert!(Asset::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( - ::transfer_from(1, sender, receiver, asset_id,), + ::transfer_from( + sender, 1, sender, receiver, asset_id, + ), Error::::ReceiverIsZeroAddress ); }); @@ -312,7 +338,9 @@ mod traits { System::set_block_number(1); assert!(Asset::::get(asset_id).is_none()); assert_noop!( - ::transfer_from(1, sender, receiver, asset_id,), + ::transfer_from( + sender, 1, sender, receiver, asset_id, + ), Error::::CollectionDoesNotExist ); }); @@ -331,7 +359,7 @@ mod traits { assert!(Asset::::get(asset_id).is_none()); assert_eq!(::owner_of(1, asset_id).unwrap(), sender); assert_ok!(::transfer_from( - 1, sender, receiver, asset_id, + sender, 1, sender, receiver, asset_id, )); assert_eq!(Asset::::get(asset_id).unwrap(), receiver); assert_eq!(::owner_of(1, asset_id).unwrap(), receiver); diff --git a/pallets/living-assets-ownership/src/traits.rs b/pallets/living-assets-ownership/src/traits.rs index dc8cf25d..8af1063d 100644 --- a/pallets/living-assets-ownership/src/traits.rs +++ b/pallets/living-assets-ownership/src/traits.rs @@ -83,11 +83,13 @@ pub trait Erc721 { /// /// # Arguments /// + /// * `origin` - The caller's address. /// * `collection_id` - The unique identifier for the collection. /// * `from` - The current owner of the asset. /// * `to` - The new owner. /// * `asset_id` - The unique identifier for the asset within the collection. fn transfer_from( + origin: H160, collection_id: CollectionId, from: H160, to: H160, From c1e7dc61b9185f3565f29a854a231cc0d97804fa Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 16:29:14 +0200 Subject: [PATCH 109/114] remove pallet_evm could from living asset pallet --- .../living-assets-ownership/src/functions.rs | 2 +- pallets/living-assets-ownership/src/lib.rs | 2 +- pallets/living-assets-ownership/src/mock.rs | 96 +-------------- pallets/living-assets-ownership/src/tests.rs | 115 +++++++----------- 4 files changed, 47 insertions(+), 168 deletions(-) diff --git a/pallets/living-assets-ownership/src/functions.rs b/pallets/living-assets-ownership/src/functions.rs index 9f37c25d..e73dc456 100644 --- a/pallets/living-assets-ownership/src/functions.rs +++ b/pallets/living-assets-ownership/src/functions.rs @@ -35,7 +35,7 @@ pub fn convert_asset_id_to_owner(value: U256) -> H160 { #[cfg(test)] mod tests { - use super::*; + use crate::{functions::convert_asset_id_to_owner, H160, U256}; #[test] fn check_convert_asset_id_to_owner() { diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index c3df4be1..e4f1bbd7 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -34,7 +34,7 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] - pub trait Config: frame_system::Config + pallet_evm::Config { + pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. type RuntimeEvent: From> + IsType<::RuntimeEvent>; diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index 93f6fa58..8354119b 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -1,18 +1,11 @@ use crate as pallet_livingassets_ownership; -use frame_support::parameter_types; -use frame_support::traits::{ConstU16, ConstU64, FindAuthor}; -use frame_support::weights::Weight; -use pallet_evm::{ - runner, EnsureAddressNever, EnsureAddressRoot, FeeCalculator, FixedGasWeightMapping, - IdentityAddressMapping, SubstrateBlockHashMapping, -}; -use sp_core::{ConstU32, H160, H256, U256}; -use sp_runtime::ConsensusEngineId; +use frame_support::traits::{ConstU16, ConstU64}; +use sp_core::{ConstU32, H256}; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, BuildStorage, }; -use sp_std::{boxed::Box, prelude::*, str::FromStr}; +use sp_std::{boxed::Box, prelude::*}; type Block = frame_system::mocking::MockBlock; type Nonce = u32; @@ -23,9 +16,6 @@ frame_support::construct_runtime!( { System: frame_system, LivingAssetsModule: pallet_livingassets_ownership, - Balances: pallet_balances, - Timestamp: pallet_timestamp, - EVM: pallet_evm, } ); @@ -40,7 +30,7 @@ impl frame_system::Config for Test { type Hash = H256; type Nonce = Nonce; type Hashing = BlakeTwo256; - type AccountId = H160; + type AccountId = u64; type Lookup = IdentityLookup; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; @@ -60,84 +50,6 @@ impl pallet_livingassets_ownership::Config for Test { type BaseURILimit = ConstU32<256>; } -parameter_types! { - pub const ExistentialDeposit: u64 = 1; -} - -impl pallet_balances::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type Balance = u64; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type ReserveIdentifier = (); - type FreezeIdentifier = (); - type MaxLocks = (); - type MaxReserves = (); - type MaxHolds = (); - type MaxFreezes = (); - type RuntimeHoldReason = (); -} - -parameter_types! { - pub const MinimumPeriod: u64 = 1000; -} - -impl pallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -pub struct FixedGasPrice; -impl FeeCalculator for FixedGasPrice { - fn min_gas_price() -> (U256, Weight) { - // Return some meaningful gas price and weight - (1_000_000_000u128.into(), Weight::from_parts(7u64, 0)) - } -} -pub struct FindAuthorTruncated; -impl FindAuthor for FindAuthorTruncated { - fn find_author<'a, I>(_digests: I) -> Option - where - I: 'a + IntoIterator, - { - Some(H160::from_str("1234500000000000000000000000000000000000").unwrap()) - } -} -const BLOCK_GAS_LIMIT: u64 = 150_000_000; -const MAX_POV_SIZE: u64 = 5 * 1024 * 1024; - -parameter_types! { - pub BlockGasLimit: U256 = U256::from(BLOCK_GAS_LIMIT); - pub const GasLimitPovSizeRatio: u64 = BLOCK_GAS_LIMIT.saturating_div(MAX_POV_SIZE); - pub WeightPerGas: Weight = Weight::from_parts(20_000, 0); -} -impl pallet_evm::Config for Test { - type FeeCalculator = FixedGasPrice; - type GasWeightMapping = FixedGasWeightMapping; - type WeightPerGas = WeightPerGas; - type BlockHashMapping = SubstrateBlockHashMapping; - type CallOrigin = EnsureAddressRoot; - type WithdrawOrigin = EnsureAddressNever; - type AddressMapping = IdentityAddressMapping; - type Currency = Balances; - type RuntimeEvent = RuntimeEvent; - type PrecompilesType = (); - type PrecompilesValue = (); - type ChainId = (); - type BlockGasLimit = BlockGasLimit; - type Runner = runner::stack::Runner; - type OnChargeTransaction = (); - type OnCreate = (); - type FindAuthor = FindAuthorTruncated; - type GasLimitPovSizeRatio = GasLimitPovSizeRatio; - type Timestamp = Timestamp; - type WeightInfo = (); -} - // Build genesis storage according to the mock runtime. pub fn new_test_ext() -> sp_io::TestExternalities { RuntimeGenesisConfig::default().build_storage().unwrap().into() diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index e20c7166..8d0c3c3a 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,17 +1,19 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, - CollectionError, Event, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, Asset, + CollectionBaseURI, CollectionError, Event, }; use frame_support::assert_ok; use sp_core::H160; type BaseURI = crate::BaseURI; +type AccountId = ::AccountId; + +const ALICE: AccountId = 0x1234; +const BOB: AccountId = 0x2234; +// use super::*; -const ALICE: &'static str = "0xffffffffffffffffffffffff0000000000000001"; -const BOB: &'static str = "0xffffffffffffffffffffffff0000000000000002"; -use super::*; #[test] fn base_uri_unexistent_collection_is_none() { new_test_ext().execute_with(|| { @@ -32,7 +34,7 @@ fn create_new_collection_should_create_sequential_collections() { for i in 0..3 { // Create the collection assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), base_uri.clone() )); @@ -48,7 +50,7 @@ fn should_set_base_uri_when_creating_new_collection() { new_test_ext().execute_with(|| { assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), base_uri.clone() )); assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); @@ -62,37 +64,25 @@ fn create_new_collections_should_emit_events_with_collection_id_consecutive() { System::set_block_number(1); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), BaseURI::default() )); - System::assert_last_event( - Event::CollectionCreated { collection_id: 0, who: H160::from_str(ALICE).unwrap() } - .into(), - ); + System::assert_last_event(Event::CollectionCreated { collection_id: 0, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), BaseURI::default() )); - System::assert_last_event( - Event::CollectionCreated { collection_id: 1, who: H160::from_str(ALICE).unwrap() } - .into(), - ); + System::assert_last_event(Event::CollectionCreated { collection_id: 1, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), BaseURI::default() )); - System::assert_last_event( - Event::CollectionCreated { collection_id: 2, who: H160::from_str(ALICE).unwrap() } - .into(), - ); + System::assert_last_event(Event::CollectionCreated { collection_id: 2, who: ALICE }.into()); assert_ok!(LivingAssetsModule::create_collection( - RuntimeOrigin::signed(H160::from_str(ALICE).unwrap()), + RuntimeOrigin::signed(ALICE), BaseURI::default() )); - System::assert_last_event( - Event::CollectionCreated { collection_id: 3, who: H160::from_str(ALICE).unwrap() } - .into(), - ); + System::assert_last_event(Event::CollectionCreated { collection_id: 3, who: ALICE }.into()); }); } @@ -156,12 +146,11 @@ mod traits { System::set_block_number(1); assert_ok!(::create_collection( - H160::from_str(ALICE).unwrap(), + ALICE, BaseURI::default(), )); System::assert_last_event( - Event::CollectionCreated { collection_id: 0, who: H160::from_str(ALICE).unwrap() } - .into(), + Event::CollectionCreated { collection_id: 0, who: ALICE }.into(), ); }); } @@ -170,51 +159,33 @@ mod traits { fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { new_test_ext().execute_with(|| { assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 0 ); assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 1 ); assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 2 ); assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 3 ); assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 4 ); assert_eq!( - ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default() - ) - .unwrap(), + ::create_collection(1, BaseURI::default()) + .unwrap(), 5 ); }); @@ -226,7 +197,7 @@ mod traits { new_test_ext().execute_with(|| { assert_ok!(::create_collection( - H160::from_str(ALICE).unwrap(), + 1, base_uri.clone() )); assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); @@ -244,11 +215,9 @@ mod traits { #[test] fn erc721_owner_of_asset_of_collection() { new_test_ext().execute_with(|| { - let collection_id = ::create_collection( - H160::from_str(ALICE).unwrap(), - BaseURI::default(), - ) - .unwrap(); + let collection_id = + ::create_collection(1, BaseURI::default()) + .unwrap(); assert_eq!( ::owner_of(collection_id, 2.into()).unwrap(), H160::from_low_u64_be(0x0000000000000002) @@ -260,7 +229,7 @@ mod traits { fn sender_is_not_current_owner_should_fail() { let asset_id = U256::from(5); let sender = H160::from_str("0000000000000000000000000000000000000006").unwrap(); - let receiver = H160::from_str(BOB).unwrap(); + let receiver = H160::from_low_u64_be(BOB); new_test_ext().execute_with(|| { System::set_block_number(1); assert!(Asset::::get(asset_id).is_none()); @@ -307,7 +276,7 @@ mod traits { fn unexistent_collection_when_transfer_from_should_fail() { let asset_id = U256::from(5); let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); - let receiver = H160::from_str(BOB).unwrap(); + let receiver = H160::from_low_u64_be(BOB); new_test_ext().execute_with(|| { System::set_block_number(1); assert!(Asset::::get(asset_id).is_none()); @@ -324,7 +293,7 @@ mod traits { hex::decode("03C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap().as_slice(), ); let sender = H160::from_str("C0F0f4ab324C46e55D02D0033343B4Be8A55532d").unwrap(); - let receiver = H160::from_str(BOB).unwrap(); + let receiver = H160::from_low_u64_be(BOB); new_test_ext().execute_with(|| { System::set_block_number(1); CollectionBaseURI::::insert(1, BaseURI::default()); @@ -352,11 +321,9 @@ mod traits { let base_uri = BaseURI::try_from("https://example.com".as_bytes().to_vec()).unwrap(); new_test_ext().execute_with(|| { - let collection_id = ::create_collection( - H160::from_str(ALICE).unwrap(), - base_uri.clone(), - ) - .unwrap(); + let collection_id = + ::create_collection(1, base_uri.clone()) + .unwrap(); assert_eq!( ::token_uri(collection_id, 2.into()).unwrap(), "https://example.com/2".as_bytes().to_vec() From 6b3940ddb42d2203a49f4e147527cc912fd2ccd6 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 16:40:13 +0200 Subject: [PATCH 110/114] change requests --- pallets/living-assets-ownership/src/lib.rs | 16 ++- pallets/living-assets-ownership/src/tests.rs | 16 +-- precompile/erc721/src/lib.rs | 14 -- precompile/erc721/src/tests.rs | 142 +------------------ 4 files changed, 18 insertions(+), 170 deletions(-) diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index e4f1bbd7..9537b2b1 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -61,11 +61,13 @@ pub mod pallet { pub(super) type CollectionBaseURI = StorageMap<_, Blake2_128Concat, CollectionId, BaseURI, OptionQuery>; + /// Asset owner #[pallet::storage] - pub(super) type Asset = StorageMap<_, Blake2_128Concat, U256, H160, OptionQuery>; + pub(super) type AssetOwner = + StorageMap<_, Blake2_128Concat, U256, H160, OptionQuery>; fn asset_owner(key: U256) -> H160 { - Asset::::get(key).unwrap_or_else(|| convert_asset_id_to_owner(key)) + AssetOwner::::get(key).unwrap_or_else(|| convert_asset_id_to_owner(key)) } /// Pallet events @@ -94,8 +96,8 @@ pub mod pallet { AssetDoesNotExist, // CannotTransferSelf, CannotTransferSelf, - // ReceiverIsZeroAddress, - ReceiverIsZeroAddress, + // TransferToNullAddress, + TransferToNullAddress, } impl AsRef<[u8]> for Error { @@ -107,7 +109,7 @@ pub mod pallet { Error::NoPermission => b"NoPermission", Error::AssetDoesNotExist => b"AssetDoesNotExist", Error::CannotTransferSelf => b"CannotTransferSelf", - Error::ReceiverIsZeroAddress => b"ReceiverIsZeroAddress", + Error::TransferToNullAddress => b"TransferToNullAddress", } } } @@ -182,9 +184,9 @@ pub mod pallet { Pallet::::collection_base_uri(collection_id).ok_or(Error::CollectionDoesNotExist)?; ensure!(asset_owner::(asset_id) == from, Error::NoPermission); ensure!(from != to, Error::CannotTransferSelf); - ensure!(to != H160::zero(), Error::ReceiverIsZeroAddress); + ensure!(to != H160::zero(), Error::TransferToNullAddress); - Asset::::set(asset_id, Some(to.clone())); + AssetOwner::::set(asset_id, Some(to.clone())); Self::deposit_event(Event::AssetTransferred { asset_id, receiver: to }); Ok(()) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 8d0c3c3a..72fedc85 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -1,7 +1,7 @@ use core::str::FromStr; use crate::{ - address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, Asset, + address_to_collection_id, collection_id_to_address, is_collection_address, mock::*, AssetOwner, CollectionBaseURI, CollectionError, Event, }; use frame_support::assert_ok; @@ -232,7 +232,7 @@ mod traits { let receiver = H160::from_low_u64_be(BOB); new_test_ext().execute_with(|| { System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); + assert!(AssetOwner::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( ::transfer_from(1, sender, receiver, asset_id,), @@ -247,7 +247,7 @@ mod traits { let sender = H160::from_str("0000000000000000000000000000000000000005").unwrap(); new_test_ext().execute_with(|| { System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); + assert!(AssetOwner::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( ::transfer_from(1, sender, sender, asset_id,), @@ -263,11 +263,11 @@ mod traits { let receiver = H160::from_str("0000000000000000000000000000000000000000").unwrap(); new_test_ext().execute_with(|| { System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); + assert!(AssetOwner::::get(asset_id).is_none()); CollectionBaseURI::::insert(1, BaseURI::default()); assert_noop!( ::transfer_from(1, sender, receiver, asset_id,), - Error::::ReceiverIsZeroAddress + Error::::TransferToNullAddress ); }); } @@ -279,7 +279,7 @@ mod traits { let receiver = H160::from_low_u64_be(BOB); new_test_ext().execute_with(|| { System::set_block_number(1); - assert!(Asset::::get(asset_id).is_none()); + assert!(AssetOwner::::get(asset_id).is_none()); assert_noop!( ::transfer_from(1, sender, receiver, asset_id,), Error::::CollectionDoesNotExist @@ -297,12 +297,12 @@ mod traits { new_test_ext().execute_with(|| { System::set_block_number(1); CollectionBaseURI::::insert(1, BaseURI::default()); - assert!(Asset::::get(asset_id).is_none()); + assert!(AssetOwner::::get(asset_id).is_none()); assert_eq!(::owner_of(1, asset_id).unwrap(), sender); assert_ok!(::transfer_from( 1, sender, receiver, asset_id, )); - assert_eq!(Asset::::get(asset_id).unwrap(), receiver); + assert_eq!(AssetOwner::::get(asset_id).unwrap(), receiver); assert_eq!(::owner_of(1, asset_id).unwrap(), receiver); System::assert_last_event(Event::AssetTransferred { asset_id, receiver }.into()); }); diff --git a/precompile/erc721/src/lib.rs b/precompile/erc721/src/lib.rs index d4712e60..f7776d41 100644 --- a/precompile/erc721/src/lib.rs +++ b/precompile/erc721/src/lib.rs @@ -93,20 +93,6 @@ where let to: H160 = input.read::
()?.into(); let asset_id: U256 = input.read()?; - // get current owner - let result = Self::owner_of(collection_id, handle)?; - let owner: H160 = match TryInto::>::try_into(result.output) { - Ok(value) => H160::from_slice(&value.as_slice()[12..32]), - Err(_) => return Err(revert("error getting owner")), - }; - let caller = handle.context().caller; - - // checks - ensure!(owner == caller, revert("caller must be the current owner")); - ensure!(owner == from, revert("sender must be the current owner")); - ensure!(from != to, revert("sender and receiver cannot be the same")); - ensure!(to != H160::zero(), revert("receiver cannot be zero address")); - AssetManager::transfer_from(collection_id, from, to, asset_id) .map_err(|err| revert(err))?; diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index 021e4d61..303273c6 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -86,150 +86,10 @@ fn token_owners_should_have_at_least_token_id_as_argument() { } mod transfer_from { + use super::*; use frame_support::assert_ok; use precompile_utils::testing::create_mock_handle; - use super::*; - - #[test] - fn caller_is_not_current_owner_should_fail() { - impl_precompile_mock_simple!( - Mock, - // owner_of result - Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), - // token_uri result - Ok(vec![]), - // transfer_from result - Ok(()) - ); - - // test data - let from = H160::repeat_byte(0xAA); - let to = H160::repeat_byte(0xBB); - let asset_id = 4; - let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - .write(Address(from)) - .write(Address(to)) - .write(U256::from(asset_id)) - .build(); - - let mut handle = create_mock_handle(input_data, 0, 0, H160::zero()); - handle.code_address = contract_address.unwrap(); - let result = Mock::execute(&mut handle); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("caller must be the current owner"),); - } - - #[test] - fn sender_is_not_current_owner_should_fail() { - impl_precompile_mock_simple!( - Mock, - // owner_of result - Ok(H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap()), - // token_uri result - Ok(vec![]), - // transfer_from result - Ok(()) - ); - - // test data - let from = H160::repeat_byte(0xAA); - let to = H160::repeat_byte(0xBB); - let asset_id = 4; - let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - .write(Address(from)) - .write(Address(to)) - .write(U256::from(asset_id)) - .build(); - - let mut handle = create_mock_handle( - input_data, - 0, - 0, - H160::from_str("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb").unwrap(), - ); - handle.code_address = contract_address.unwrap(); - let result = Mock::execute(&mut handle); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("sender must be the current owner"),); - } - - #[test] - fn receiver_is_the_current_owner_should_fail() { - impl_precompile_mock_simple!( - Mock, - // owner_of result - Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // token_uri result - Ok(vec![]), - // transfer_from result - Ok(()) - ); - - // test data - let from = H160::repeat_byte(0xAA); - let to = H160::repeat_byte(0xAA); - let asset_id = 4; - let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - .write(Address(from)) - .write(Address(to)) - .write(U256::from(asset_id)) - .build(); - - let mut handle = create_mock_handle( - input_data, - 0, - 0, - H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), - ); - handle.code_address = contract_address.unwrap(); - let result = Mock::execute(&mut handle); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("sender and receiver cannot be the same")); - } - - #[test] - fn receiver_is_the_zero_address_should_fail() { - impl_precompile_mock_simple!( - Mock, - // owner_of result - Ok(H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()), - // token_uri result - Ok(vec![]), - // transfer_from result - Ok(()) - ); - - // test data - let from = H160::repeat_byte(0xAA); - let to = H160::repeat_byte(0x0); - let asset_id = 4; - let contract_address = H160::from_str("ffffffffffffffffffffffff0000000000000005"); - - let input_data = EvmDataWriter::new_with_selector(Action::TransferFrom) - .write(Address(from)) - .write(Address(to)) - .write(U256::from(asset_id)) - .build(); - - let mut handle = create_mock_handle( - input_data, - 0, - 0, - H160::from_str("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), - ); - handle.code_address = contract_address.unwrap(); - let result = Mock::execute(&mut handle); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), revert("receiver cannot be zero address")); - } - #[test] fn send_value_as_money_should_fail() { impl_precompile_mock_simple!( From 191bd15e0efd5a1e5e251b9642e3cda2c1f01313 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 16:51:12 +0200 Subject: [PATCH 111/114] change requests --- pallets/living-assets-ownership/src/tests.rs | 61 +++++++++++++------- precompile/erc721/src/tests.rs | 6 +- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/pallets/living-assets-ownership/src/tests.rs b/pallets/living-assets-ownership/src/tests.rs index 740cde94..ab812b74 100644 --- a/pallets/living-assets-ownership/src/tests.rs +++ b/pallets/living-assets-ownership/src/tests.rs @@ -12,7 +12,6 @@ type AccountId = ::AccountId; const ALICE: AccountId = 0x1234; const BOB: AccountId = 0x2234; -// use super::*; #[test] fn base_uri_unexistent_collection_is_none() { @@ -159,33 +158,51 @@ mod traits { fn living_assets_ownership_trait_id_of_new_collection_should_be_consecutive() { new_test_ext().execute_with(|| { assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 0 ); assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 1 ); assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 2 ); assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 3 ); assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 4 ); assert_eq!( - ::create_collection(1, BaseURI::default()) - .unwrap(), + ::create_collection( + ALICE, + BaseURI::default() + ) + .unwrap(), 5 ); }); @@ -197,7 +214,7 @@ mod traits { new_test_ext().execute_with(|| { assert_ok!(::create_collection( - 1, + ALICE, base_uri.clone() )); assert_eq!(LivingAssetsModule::collection_base_uri(0).unwrap(), base_uri); @@ -215,9 +232,11 @@ mod traits { #[test] fn erc721_owner_of_asset_of_collection() { new_test_ext().execute_with(|| { - let collection_id = - ::create_collection(1, BaseURI::default()) - .unwrap(); + let collection_id = ::create_collection( + ALICE, + BaseURI::default(), + ) + .unwrap(); assert_eq!( ::owner_of(collection_id, 2.into()).unwrap(), H160::from_low_u64_be(0x0000000000000002) @@ -349,9 +368,11 @@ mod traits { let base_uri = BaseURI::try_from("https://example.com".as_bytes().to_vec()).unwrap(); new_test_ext().execute_with(|| { - let collection_id = - ::create_collection(1, base_uri.clone()) - .unwrap(); + let collection_id = ::create_collection( + ALICE, + base_uri.clone(), + ) + .unwrap(); assert_eq!( ::token_uri(collection_id, 2.into()).unwrap(), "https://example.com/2".as_bytes().to_vec() diff --git a/precompile/erc721/src/tests.rs b/precompile/erc721/src/tests.rs index bee4ec9b..df6125ae 100644 --- a/precompile/erc721/src/tests.rs +++ b/precompile/erc721/src/tests.rs @@ -224,7 +224,7 @@ mod helpers { /// # Arguments /// /// * `$name`: An identifier to name the precompile mock type. - /// * `$owner_of`: A closure that takes `collection_id` and `asset_id` and returns a `Result`. + /// * `$owner_of_collection`: A closure that takes `collection_id` and `asset_id` and returns a `Result`. /// /// # Example /// @@ -236,7 +236,7 @@ mod helpers { /// ``` #[macro_export] macro_rules! impl_precompile_mock { - ($name:ident, $owner_of:expr, $token_uri:expr, $transfer_from:expr) => { + ($name:ident, $owner_of_collection:expr, $token_uri:expr, $transfer_from:expr) => { struct Erc721Mock; impl pallet_living_assets_ownership::traits::Erc721 for Erc721Mock { @@ -246,7 +246,7 @@ mod helpers { collection_id: CollectionId, asset_id: U256, ) -> Result { - ($owner_of)(collection_id, asset_id) + ($owner_of_collection)(collection_id, asset_id) } fn token_uri( From 736aa55dab3a5446a5d9c4f108c9ca53d29ccd52 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 16:52:21 +0200 Subject: [PATCH 112/114] change requests --- pallets/living-assets-ownership/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/living-assets-ownership/src/mock.rs b/pallets/living-assets-ownership/src/mock.rs index 8354119b..9bb080b6 100644 --- a/pallets/living-assets-ownership/src/mock.rs +++ b/pallets/living-assets-ownership/src/mock.rs @@ -36,7 +36,7 @@ impl frame_system::Config for Test { type BlockHashCount = ConstU64<250>; type Version = (); type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; + type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); From dbb52e70b9645ee121df01d833c6b2d28dbe43b6 Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 17:31:06 +0200 Subject: [PATCH 113/114] change requests --- Cargo.lock | 2 -- pallets/living-assets-ownership/Cargo.toml | 9 --------- pallets/living-assets-ownership/src/lib.rs | 19 ------------------- 3 files changed, 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3434b58d..e0477b00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7181,8 +7181,6 @@ dependencies = [ "hex", "hex-literal 0.4.1", "pallet-balances", - "pallet-ethereum", - "pallet-evm", "pallet-timestamp", "parity-scale-codec", "scale-info", diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index e352e5e6..b4f16d99 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -16,10 +16,6 @@ hex-literal = { workspace = true, optional = true} parity-scale-codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } -# Frontier -pallet-evm = { workspace = true } -pallet-ethereum = { workspace = true } - # Substrate frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } @@ -57,13 +53,8 @@ std = [ "sp-arithmetic/std", "sp-core/std", "sp-std/std", - # Frontier FRAME - "pallet-ethereum/std", - "pallet-evm/std", ] try-runtime = [ "frame-system/try-runtime", "frame-support/try-runtime", - "pallet-evm/try-runtime", - "pallet-ethereum/try-runtime", ] diff --git a/pallets/living-assets-ownership/src/lib.rs b/pallets/living-assets-ownership/src/lib.rs index 88bf8643..6acbb7c3 100644 --- a/pallets/living-assets-ownership/src/lib.rs +++ b/pallets/living-assets-ownership/src/lib.rs @@ -131,25 +131,6 @@ pub mod pallet { } } - /// Errors that can occur when managing collections. - /// - /// - `CollectionIdOverflow`: The ID for the new collection would overflow. - /// - `UnknownError`: An unspecified error occurred. - #[derive(Debug, PartialEq)] - pub enum CollectionManagerError { - CollectionIdOverflow, - UnknownError, - } - - impl AsRef<[u8]> for CollectionManagerError { - fn as_ref(&self) -> &[u8] { - match self { - CollectionManagerError::CollectionIdOverflow => b"CollectionIdOverflow", - CollectionManagerError::UnknownError => b"UnknownError", - } - } - } - impl traits::CollectionManager for Pallet { type Error = Error; type AccountId = T::AccountId; From b7d8992dc3f2a0f10d665ab598694a089f3dd72d Mon Sep 17 00:00:00 2001 From: magecnion Date: Thu, 24 Aug 2023 17:32:10 +0200 Subject: [PATCH 114/114] change requests --- Cargo.lock | 2 -- pallets/living-assets-ownership/Cargo.toml | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e0477b00..4ebe695c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7180,8 +7180,6 @@ dependencies = [ "frame-system", "hex", "hex-literal 0.4.1", - "pallet-balances", - "pallet-timestamp", "parity-scale-codec", "scale-info", "serde", diff --git a/pallets/living-assets-ownership/Cargo.toml b/pallets/living-assets-ownership/Cargo.toml index b4f16d99..73aaadac 100644 --- a/pallets/living-assets-ownership/Cargo.toml +++ b/pallets/living-assets-ownership/Cargo.toml @@ -20,8 +20,6 @@ scale-info = { workspace = true, features = ["derive"] } frame-benchmarking = { workspace = true, optional = true } frame-support = { workspace = true } frame-system = { workspace = true } -pallet-balances = { workspace = true } -pallet-timestamp = { workspace = true } sp-arithmetic = { workspace = true } sp-core = { workspace = true } sp-std = { workspace = true } @@ -48,13 +46,11 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", - "pallet-balances/std", - "pallet-timestamp/std", "sp-arithmetic/std", "sp-core/std", "sp-std/std", ] try-runtime = [ "frame-system/try-runtime", - "frame-support/try-runtime", + "frame-support/try-runtime" ]