diff --git a/Cargo.lock b/Cargo.lock index a9098e3a..267f57cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3333,6 +3333,10 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-randomness-collective-flip", + "pallet-rmrk-core", + "pallet-rmrk-equip", + "pallet-rmrk-market", + "pallet-rmrk-rpc-runtime-api", "pallet-scheduler", "pallet-session", "pallet-simple-inflation", @@ -3343,9 +3347,11 @@ dependencies = [ "pallet-transaction-payment", "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", + "pallet-uniques", "pallet-utility", "pallet-vesting", "parity-scale-codec", + "rmrk-traits", "scale-info", "separator", "serde", @@ -3937,6 +3943,10 @@ dependencies = [ "pallet-ethereum", "pallet-evm", "pallet-fees-split", + "pallet-rmrk-core", + "pallet-rmrk-equip", + "pallet-rmrk-rpc", + "pallet-rmrk-rpc-runtime-api", "pallet-simple-inflation", "pallet-transaction-payment-rpc", "parity-scale-codec", @@ -3944,6 +3954,7 @@ dependencies = [ "polkadot-parachain", "polkadot-primitives", "polkadot-service", + "rmrk-traits", "sc-basic-authorship", "sc-chain-spec", "sc-cli", @@ -6923,6 +6934,94 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-rmrk-core" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-uniques", + "parity-scale-codec", + "rmrk-traits", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-rmrk-equip" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-rmrk-core", + "pallet-uniques", + "parity-scale-codec", + "rmrk-traits", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-rmrk-market" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-rmrk-core", + "pallet-uniques", + "parity-scale-codec", + "rmrk-traits", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + +[[package]] +name = "pallet-rmrk-rpc" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "jsonrpsee", + "pallet-rmrk-rpc-runtime-api", + "parity-scale-codec", + "rmrk-traits", + "sc-client-api", + "sc-rpc", + "scale-info", + "sp-api", + "sp-blockchain", + "sp-runtime", +] + +[[package]] +name = "pallet-rmrk-rpc-runtime-api" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "parity-scale-codec", + "rmrk-traits", + "scale-info", + "serde", + "sp-api", + "sp-core", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -7180,6 +7279,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-uniques" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.36#cb4f2491b00af7d7817f3a54209c26b20faa1f51" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-utility" version = "4.0.0-dev" @@ -9285,6 +9399,22 @@ dependencies = [ "syn", ] +[[package]] +name = "rmrk-traits" +version = "0.0.1" +source = "git+https://github.com/web3labs/rmrk-substrate?branch=polkadot-v0.9.36#4f9f37a59aa41ff7964c2077a9f09e18141a24ca" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "serde", + "sp-runtime", + "sp-std", +] + [[package]] name = "rocksdb" version = "0.19.0" diff --git a/node/Cargo.toml b/node/Cargo.toml index 4dd41182..5ca965dc 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -126,6 +126,14 @@ pallet-ethereum = { default-features = false, git = "https://github.com/web3labs pallet-dynamic-fee = { default-features = false, git = "https://github.com/web3labs/frontier", branch = "polkadot-v0.9.36" } pallet-base-fee = { default-features = false, git = "https://github.com/web3labs/frontier", branch = "polkadot-v0.9.36" } +#rmrk +pallet-rmrk-core = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36"} +pallet-rmrk-equip = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36" } +rmrk-traits = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36" } +pallet-rmrk-rpc = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36" } +pallet-rmrk-rpc-runtime-api = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36"} + + snow-runtime = { path = "../runtime/snow", default-features = false, features = [ "std", ] } diff --git a/node/src/rpc/arctic.rs b/node/src/rpc/arctic.rs new file mode 100644 index 00000000..f3659f88 --- /dev/null +++ b/node/src/rpc/arctic.rs @@ -0,0 +1,112 @@ +//! Ice RPCs implementation. + +use super::FullDeps; +use crate::primitives::*; +use fc_rpc::{ + Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, + NetApiServer, Web3, Web3ApiServer, +}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; +use sc_client_api::{AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider}; +pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +use sc_transaction_pool::ChainApi; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::BlakeTwo256; +use substrate_frame_rpc_system::{System, SystemApiServer}; + +/// Instantiate all RPC extensions. +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, +) -> Result, Box> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + StorageProvider + + HeaderMetadata + + BlockchainEvents + + Send + + Sync + + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + BlockBuilder, + P: TransactionPool + Sync + Send + 'static, + BE: Backend + 'static, + BE::State: StateBackend, + BE::Blockchain: BlockchainBackend, + A: ChainApi + 'static, +{ + let mut io = RpcModule::new(()); + let FullDeps { + client, + pool, + graph, + network, + deny_unsafe, + is_authority, + frontier_backend, + filter_pool, + fee_history_limit, + fee_history_cache, + overrides, + block_data_cache, + } = deps; + + io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + io.merge(TransactionPayment::new(client.clone()).into_rpc())?; + + io.merge( + Eth::new( + client.clone(), + pool.clone(), + graph, + Some(arctic_runtime::TransactionConverter), + network.clone(), + Default::default(), + overrides.clone(), + frontier_backend.clone(), + is_authority, + block_data_cache.clone(), + fee_history_cache, + fee_history_limit, + 10, + ) + .into_rpc(), + )?; + + let max_past_logs: u32 = 10_000; + let max_stored_filters: usize = 500; + io.merge( + EthFilter::new( + client.clone(), + frontier_backend, + filter_pool, + max_stored_filters, + max_past_logs, + block_data_cache, + ) + .into_rpc(), + )?; + + io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?; + + io.merge(Web3::new(client.clone()).into_rpc())?; + + io.merge( + EthPubSub::new(pool, client, network, subscription_task_executor, overrides).into_rpc(), + )?; + // need to import types from runtime which is not ideal. + // io.merge(Rmrk::new(client.clone()).into_rpc())?; + + Ok(io) +} diff --git a/node/src/rpc/frost.rs b/node/src/rpc/frost.rs new file mode 100644 index 00000000..eabc8eb6 --- /dev/null +++ b/node/src/rpc/frost.rs @@ -0,0 +1,148 @@ +//! Ice RPCs implementation. + +use super::FullDeps; +use crate::primitives::*; +use fc_rpc::{ + Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, + NetApiServer, Web3, Web3ApiServer, +}; +use frost_runtime::{ + CollectionSymbolLimit, KeyLimit, MaxCollectionsEquippablePerPart, MaxPropertiesPerTheme, + PartsLimit, UniquesStringLimit, ValueLimit, +}; +use jsonrpsee::RpcModule; +use pallet_rmrk_rpc::{Rmrk, RmrkApiServer}; +use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; +use rmrk_traits::{ + primitives::{CollectionId, NftId, PartId}, + BaseInfo, CollectionInfo, NftInfo, PartType, PropertyInfo, ResourceInfo, Theme, ThemeProperty, +}; +use sc_client_api::{AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider}; +pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +use sc_transaction_pool::ChainApi; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::BlakeTwo256; +use sp_runtime::{BoundedVec, Permill}; +use substrate_frame_rpc_system::{System, SystemApiServer}; +/// Instantiate all RPC extensions. +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, +) -> Result, Box> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + StorageProvider + + HeaderMetadata + + BlockchainEvents + + Send + + Sync + + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + BlockBuilder, + C::Api: pallet_rmrk_rpc_runtime_api::RmrkApi< + Block, + AccountId, + CollectionInfo< + BoundedVec, + BoundedVec, + AccountId, + >, + NftInfo, CollectionId, NftId>, + ResourceInfo, BoundedVec>, + PropertyInfo, BoundedVec>, + BaseInfo>, + PartType< + BoundedVec, + BoundedVec, + >, + Theme< + BoundedVec, + BoundedVec>, MaxPropertiesPerTheme>, + >, + >, + P: TransactionPool + Sync + Send + 'static, + BE: Backend + 'static, + BE::State: StateBackend, + BE::Blockchain: BlockchainBackend, + A: ChainApi + 'static, +{ + let mut io = RpcModule::new(()); + let FullDeps { + client, + pool, + graph, + network, + deny_unsafe, + is_authority, + frontier_backend, + filter_pool, + fee_history_limit, + fee_history_cache, + overrides, + block_data_cache, + } = deps; + + io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + io.merge(TransactionPayment::new(client.clone()).into_rpc())?; + + io.merge( + Eth::new( + client.clone(), + pool.clone(), + graph, + Some(arctic_runtime::TransactionConverter), + network.clone(), + Default::default(), + overrides.clone(), + frontier_backend.clone(), + is_authority, + block_data_cache.clone(), + fee_history_cache, + fee_history_limit, + 10, + ) + .into_rpc(), + )?; + + let max_past_logs: u32 = 10_000; + let max_stored_filters: usize = 500; + io.merge( + EthFilter::new( + client.clone(), + frontier_backend, + filter_pool, + max_stored_filters, + max_past_logs, + block_data_cache, + ) + .into_rpc(), + )?; + + io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?; + + io.merge(Web3::new(client.clone()).into_rpc())?; + + io.merge( + EthPubSub::new( + pool, + client.clone(), + network, + subscription_task_executor, + overrides, + ) + .into_rpc(), + )?; + io.merge(Rmrk::new(client.clone()).into_rpc())?; + + Ok(io) +} diff --git a/node/src/rpc.rs b/node/src/rpc/mod.rs similarity index 52% rename from node/src/rpc.rs rename to node/src/rpc/mod.rs index 33bda630..221e53a1 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc/mod.rs @@ -1,28 +1,22 @@ //! Ice RPCs implementation. - +pub mod arctic; +pub mod frost; +pub mod snow; use fc_rpc::{ - Eth, EthApiServer, EthBlockDataCacheTask, EthFilter, EthFilterApiServer, EthPubSub, - EthPubSubApiServer, Net, NetApiServer, OverrideHandle, RuntimeApiStorageOverride, - SchemaV1Override, SchemaV2Override, SchemaV3Override, StorageOverride, Web3, Web3ApiServer, + EthBlockDataCacheTask, OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, + SchemaV2Override, SchemaV3Override, StorageOverride, }; use fc_rpc_core::types::{FeeHistoryCache, FilterPool}; use fp_storage::EthereumStorageSchema; -use jsonrpsee::RpcModule; -use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; -use sc_client_api::{AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider}; +use sc_client_api::{AuxStore, Backend, StateBackend, StorageProvider}; use sc_network::NetworkService; pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; use sc_transaction_pool::{ChainApi, Pool}; -use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; -use sp_block_builder::BlockBuilder; -use sp_blockchain::{ - Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, -}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; use sp_runtime::traits::BlakeTwo256; use std::collections::BTreeMap; use std::sync::Arc; -use substrate_frame_rpc_system::{System, SystemApiServer}; use crate::primitives::*; @@ -114,92 +108,3 @@ pub struct FullDeps { /// Cache for Ethereum block data. pub block_data_cache: Arc>, } - -/// Instantiate all RPC extensions. -pub fn create_full( - deps: FullDeps, - subscription_task_executor: SubscriptionTaskExecutor, -) -> Result, Box> -where - C: ProvideRuntimeApi - + HeaderBackend - + AuxStore - + StorageProvider - + HeaderMetadata - + BlockchainEvents - + Send - + Sync - + 'static, - C::Api: substrate_frame_rpc_system::AccountNonceApi - + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi - + fp_rpc::ConvertTransactionRuntimeApi - + fp_rpc::EthereumRuntimeRPCApi - + BlockBuilder, - P: TransactionPool + Sync + Send + 'static, - BE: Backend + 'static, - BE::State: StateBackend, - BE::Blockchain: BlockchainBackend, - A: ChainApi + 'static, -{ - let mut io = RpcModule::new(()); - let FullDeps { - client, - pool, - graph, - network, - deny_unsafe, - is_authority, - frontier_backend, - filter_pool, - fee_history_limit, - fee_history_cache, - overrides, - block_data_cache, - } = deps; - - io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; - io.merge(TransactionPayment::new(client.clone()).into_rpc())?; - - io.merge( - Eth::new( - client.clone(), - pool.clone(), - graph, - Some(arctic_runtime::TransactionConverter), - network.clone(), - Default::default(), - overrides.clone(), - frontier_backend.clone(), - is_authority, - block_data_cache.clone(), - fee_history_cache, - fee_history_limit, - 10, - ) - .into_rpc(), - )?; - - let max_past_logs: u32 = 10_000; - let max_stored_filters: usize = 500; - io.merge( - EthFilter::new( - client.clone(), - frontier_backend, - filter_pool, - max_stored_filters, - max_past_logs, - block_data_cache, - ) - .into_rpc(), - )?; - - io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?; - - io.merge(Web3::new(client.clone()).into_rpc())?; - - io.merge( - EthPubSub::new(pool, client, network, subscription_task_executor, overrides).into_rpc(), - )?; - - Ok(io) -} diff --git a/node/src/rpc/snow.rs b/node/src/rpc/snow.rs new file mode 100644 index 00000000..16cbf675 --- /dev/null +++ b/node/src/rpc/snow.rs @@ -0,0 +1,114 @@ +//! Ice RPCs implementation. + +use fc_rpc::{ + Eth, EthApiServer, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, Net, + NetApiServer, Web3, Web3ApiServer, +}; +use jsonrpsee::RpcModule; +use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; +use sc_client_api::{AuxStore, Backend, BlockchainEvents, StateBackend, StorageProvider}; +pub use sc_rpc::{DenyUnsafe, SubscriptionTaskExecutor}; +use sc_transaction_pool::ChainApi; +use sc_transaction_pool_api::TransactionPool; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{ + Backend as BlockchainBackend, Error as BlockChainError, HeaderBackend, HeaderMetadata, +}; +use sp_runtime::traits::BlakeTwo256; +use substrate_frame_rpc_system::{System, SystemApiServer}; + +use crate::primitives::*; + +use super::FullDeps; + +/// Instantiate all RPC extensions. +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, +) -> Result, Box> +where + C: ProvideRuntimeApi + + HeaderBackend + + AuxStore + + StorageProvider + + HeaderMetadata + + BlockchainEvents + + Send + + Sync + + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi + + pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi + + fp_rpc::ConvertTransactionRuntimeApi + + fp_rpc::EthereumRuntimeRPCApi + + BlockBuilder, + P: TransactionPool + Sync + Send + 'static, + BE: Backend + 'static, + BE::State: StateBackend, + BE::Blockchain: BlockchainBackend, + A: ChainApi + 'static, +{ + let mut io = RpcModule::new(()); + let FullDeps { + client, + pool, + graph, + network, + deny_unsafe, + is_authority, + frontier_backend, + filter_pool, + fee_history_limit, + fee_history_cache, + overrides, + block_data_cache, + } = deps; + + io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; + io.merge(TransactionPayment::new(client.clone()).into_rpc())?; + + io.merge( + Eth::new( + client.clone(), + pool.clone(), + graph, + Some(arctic_runtime::TransactionConverter), + network.clone(), + Default::default(), + overrides.clone(), + frontier_backend.clone(), + is_authority, + block_data_cache.clone(), + fee_history_cache, + fee_history_limit, + 10, + ) + .into_rpc(), + )?; + + let max_past_logs: u32 = 10_000; + let max_stored_filters: usize = 500; + io.merge( + EthFilter::new( + client.clone(), + frontier_backend, + filter_pool, + max_stored_filters, + max_past_logs, + block_data_cache, + ) + .into_rpc(), + )?; + + io.merge(Net::new(client.clone(), network.clone(), true).into_rpc())?; + + io.merge(Web3::new(client.clone()).into_rpc())?; + + io.merge( + EthPubSub::new(pool, client, network, subscription_task_executor, overrides).into_rpc(), + )?; + // need to import types from runtime which is not ideal. + // io.merge(Rmrk::new(client.clone()).into_rpc())?; + + Ok(io) +} diff --git a/node/src/service/parachain.rs b/node/src/service/parachain.rs index c2c738b2..24e5b314 100644 --- a/node/src/service/parachain.rs +++ b/node/src/service/parachain.rs @@ -423,6 +423,7 @@ where let client = client.clone(); let network = network.clone(); let transaction_pool = transaction_pool.clone(); + let name = parachain_config.impl_name.clone(); Box::new(move |deny_unsafe, subscription| { let deps = crate::rpc::FullDeps { @@ -439,8 +440,12 @@ where block_data_cache: block_data_cache.clone(), overrides: overrides.clone(), }; - - crate::rpc::create_full(deps, subscription).map_err(Into::into) + let rpc = if name.starts_with("snow") { + crate::rpc::snow::create_full(deps, subscription).map_err(Into::into) + } else { + crate::rpc::arctic::create_full(deps, subscription).map_err(Into::into) + }; + rpc }) }; diff --git a/node/src/service/solo.rs b/node/src/service/solo.rs index f4c26eb1..296e8c96 100644 --- a/node/src/service/solo.rs +++ b/node/src/service/solo.rs @@ -302,7 +302,7 @@ pub fn start_frost_node(config: Configuration) -> Result(Into::into)?; Ok(io) diff --git a/runtime/common/assets-evm-utils/src/data.rs b/runtime/common/assets-evm-utils/src/data.rs index 23ed1de4..e17d4972 100644 --- a/runtime/common/assets-evm-utils/src/data.rs +++ b/runtime/common/assets-evm-utils/src/data.rs @@ -31,15 +31,15 @@ use sp_std::{convert::TryInto, vec, vec::Vec}; pub struct Address(pub H160); impl From for Address { - fn from(a: H160) -> Address { - Address(a) - } + fn from(a: H160) -> Address { + Address(a) + } } impl From
for H160 { - fn from(a: Address) -> H160 { - a.0 - } + fn from(a: Address) -> H160 { + a.0 + } } /// The `bytes`/`string` type of Solidity. @@ -49,154 +49,154 @@ impl From
for H160 { pub struct Bytes(pub Vec); impl Bytes { - /// Interpret as `bytes`. - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } - - /// Interpret as `string`. - /// Can fail if the string is not valid UTF8. - pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> { - sp_std::str::from_utf8(&self.0) - } + /// Interpret as `bytes`. + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + /// Interpret as `string`. + /// Can fail if the string is not valid UTF8. + pub fn as_str(&self) -> Result<&str, sp_std::str::Utf8Error> { + sp_std::str::from_utf8(&self.0) + } } impl From<&[u8]> for Bytes { - fn from(a: &[u8]) -> Self { - Self(a.to_owned()) - } + fn from(a: &[u8]) -> Self { + Self(a.to_owned()) + } } impl From<&str> for Bytes { - fn from(a: &str) -> Self { - a.as_bytes().into() - } + fn from(a: &str) -> Self { + a.as_bytes().into() + } } impl From for Vec { - fn from(b: Bytes) -> Vec { - b.0 - } + fn from(b: Bytes) -> Vec { + b.0 + } } /// Wrapper around an EVM input slice, helping to parse it. /// Provide functions to parse common types. #[derive(Clone, Copy, Debug)] pub struct EvmDataReader<'a> { - input: &'a [u8], - cursor: usize, + input: &'a [u8], + cursor: usize, } impl<'a> EvmDataReader<'a> { - /// Create a new input parser. - pub fn new(input: &'a [u8]) -> Self { - Self { input, cursor: 0 } - } - - /// Create a new input parser from a selector-initial input. - pub fn read_selector(input: &'a [u8]) -> EvmResult - where - T: num_enum::TryFromPrimitive, - { - if input.len() < 4 { - return Err(revert("tried to parse selector out of bounds")); - } - - let mut buffer = [0u8; 4]; - buffer.copy_from_slice(&input[0..4]); - let selector = T::try_from_primitive(u32::from_be_bytes(buffer)).map_err(|_| { - log::trace!( - target: "precompile-utils", - "Failed to match function selector for {}", - type_name::() - ); - revert("unknown selector") - })?; - - Ok(selector) - } - - /// Create a new input parser from a selector-initial input. - pub fn new_skip_selector(input: &'a [u8]) -> EvmResult { - if input.len() < 4 { - return Err(revert("input is too short")); - } - - Ok(Self::new(&input[4..])) - } - - /// Check the input has at least the correct amount of arguments before the end (32 bytes values). - pub fn expect_arguments(&self, args: usize) -> EvmResult { - if self.input.len() >= self.cursor + args * 32 { - Ok(()) - } else { - Err(revert("input doesn't match expected length")) - } - } - - /// Read data from the input. - pub fn read(&mut self) -> EvmResult { - T::read(self) - } - - /// Read raw bytes from the input. - /// Doesn't handle any alignment checks, prefer using `read` instead of possible. - /// Returns an error if trying to parse out of bounds. - pub fn read_raw_bytes(&mut self, len: usize) -> EvmResult<&[u8]> { - let range = self.move_cursor(len)?; - - let data = self - .input - .get(range) - .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; - - Ok(data) - } - - /// Reads a pointer, returning a reader targetting the pointed location. - pub fn read_pointer(&mut self) -> EvmResult { - let offset: usize = self - .read::() - .map_err(|_| revert("tried to parse array offset out of bounds"))? - .try_into() - .map_err(|_| revert("array offset is too large"))?; - - if offset >= self.input.len() { - return Err(revert("pointer points out of bounds")); - } - - Ok(Self { - input: &self.input[offset..], - cursor: 0, - }) - } - - /// Read remaining bytes - pub fn read_till_end(&mut self) -> EvmResult<&[u8]> { - let range = self.move_cursor(self.input.len() - self.cursor)?; - - let data = self - .input - .get(range) - .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; - - Ok(data) - } - - /// Move the reading cursor with provided length, and return a range from the previous cursor - /// location to the new one. - /// Checks cursor overflows. - fn move_cursor(&mut self, len: usize) -> EvmResult> { - let start = self.cursor; - let end = self - .cursor - .checked_add(len) - .ok_or_else(|| revert("data reading cursor overflow"))?; - - self.cursor = end; - - Ok(start..end) - } + /// Create a new input parser. + pub fn new(input: &'a [u8]) -> Self { + Self { input, cursor: 0 } + } + + /// Create a new input parser from a selector-initial input. + pub fn read_selector(input: &'a [u8]) -> EvmResult + where + T: num_enum::TryFromPrimitive, + { + if input.len() < 4 { + return Err(revert("tried to parse selector out of bounds")); + } + + let mut buffer = [0u8; 4]; + buffer.copy_from_slice(&input[0..4]); + let selector = T::try_from_primitive(u32::from_be_bytes(buffer)).map_err(|_| { + log::trace!( + target: "precompile-utils", + "Failed to match function selector for {}", + type_name::() + ); + revert("unknown selector") + })?; + + Ok(selector) + } + + /// Create a new input parser from a selector-initial input. + pub fn new_skip_selector(input: &'a [u8]) -> EvmResult { + if input.len() < 4 { + return Err(revert("input is too short")); + } + + Ok(Self::new(&input[4..])) + } + + /// Check the input has at least the correct amount of arguments before the end (32 bytes values). + pub fn expect_arguments(&self, args: usize) -> EvmResult { + if self.input.len() >= self.cursor + args * 32 { + Ok(()) + } else { + Err(revert("input doesn't match expected length")) + } + } + + /// Read data from the input. + pub fn read(&mut self) -> EvmResult { + T::read(self) + } + + /// Read raw bytes from the input. + /// Doesn't handle any alignment checks, prefer using `read` instead of possible. + /// Returns an error if trying to parse out of bounds. + pub fn read_raw_bytes(&mut self, len: usize) -> EvmResult<&[u8]> { + let range = self.move_cursor(len)?; + + let data = self + .input + .get(range) + .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; + + Ok(data) + } + + /// Reads a pointer, returning a reader targetting the pointed location. + pub fn read_pointer(&mut self) -> EvmResult { + let offset: usize = self + .read::() + .map_err(|_| revert("tried to parse array offset out of bounds"))? + .try_into() + .map_err(|_| revert("array offset is too large"))?; + + if offset >= self.input.len() { + return Err(revert("pointer points out of bounds")); + } + + Ok(Self { + input: &self.input[offset..], + cursor: 0, + }) + } + + /// Read remaining bytes + pub fn read_till_end(&mut self) -> EvmResult<&[u8]> { + let range = self.move_cursor(self.input.len() - self.cursor)?; + + let data = self + .input + .get(range) + .ok_or_else(|| revert("tried to parse raw bytes out of bounds"))?; + + Ok(data) + } + + /// Move the reading cursor with provided length, and return a range from the previous cursor + /// location to the new one. + /// Checks cursor overflows. + fn move_cursor(&mut self, len: usize) -> EvmResult> { + let start = self.cursor; + let end = self + .cursor + .checked_add(len) + .ok_or_else(|| revert("data reading cursor overflow"))?; + + self.cursor = end; + + Ok(start..end) + } } /// Help build an EVM input/output data. @@ -207,211 +207,211 @@ impl<'a> EvmDataReader<'a> { /// prevent to have a `build` function that don't clone the output. #[derive(Clone, Debug)] pub struct EvmDataWriter { - pub(crate) data: Vec, - offset_data: Vec, - selector: Option, + pub(crate) data: Vec, + offset_data: Vec, + selector: Option, } #[derive(Clone, Debug)] struct OffsetDatum { - // Offset location in the container data. - offset_position: usize, - // Data pointed by the offset that must be inserted at the end of container data. - data: Vec, - // Inside of arrays, the offset is not from the start of array data (length), but from the start - // of the item. This shift allow to correct this. - offset_shift: usize, + // Offset location in the container data. + offset_position: usize, + // Data pointed by the offset that must be inserted at the end of container data. + data: Vec, + // Inside of arrays, the offset is not from the start of array data (length), but from the start + // of the item. This shift allow to correct this. + offset_shift: usize, } impl EvmDataWriter { - /// Creates a new empty output builder (without selector). - pub fn new() -> Self { - Self { - data: vec![], - offset_data: vec![], - selector: None, - } - } - - /// Creates a new empty output builder with provided selector. - /// Selector will only be appended before the data when calling - /// `build` to not mess with the offsets. - pub fn new_with_selector(selector: impl Into) -> Self { - Self { - data: vec![], - offset_data: vec![], - selector: Some(selector.into()), - } - } - - /// Return the built data. - pub fn build(mut self) -> Vec { - Self::bake_offsets(&mut self.data, self.offset_data); - - if let Some(selector) = self.selector { - let mut output = selector.to_be_bytes().to_vec(); - output.append(&mut self.data); - output - } else { - self.data - } - } - - /// Add offseted data at the end of this writer's data, updating the offsets. - fn bake_offsets(output: &mut Vec, offsets: Vec) { - for mut offset_datum in offsets { - let offset_position = offset_datum.offset_position; - let offset_position_end = offset_position + 32; - - // The offset is the distance between the start of the data and the - // start of the pointed data (start of a struct, length of an array). - // Offsets in inner data are relative to the start of their respective "container". - // However in arrays the "container" is actually the item itself instead of the whole - // array, which is corrected by `offset_shift`. - let free_space_offset = output.len() - offset_datum.offset_shift; - - // Override dummy offset to the offset it will be in the final output. - U256::from(free_space_offset) - .to_big_endian(&mut output[offset_position..offset_position_end]); - - // Append this data at the end of the current output. - output.append(&mut offset_datum.data); - } - } - - /// Write arbitrary bytes. - /// Doesn't handle any alignement checks, prefer using `write` instead if possible. - fn write_raw_bytes(mut self, value: &[u8]) -> Self { - self.data.extend_from_slice(value); - self - } - - /// Write data of requested type. - pub fn write(mut self, value: T) -> Self { - T::write(&mut self, value); - self - } - - /// Writes a pointer to given data. - /// The data will be appended when calling `build`. - /// Initially write a dummy value as offset in this writer's data, which will be replaced by - /// the correct offset once the pointed data is appended. - /// - /// Takes `&mut self` since its goal is to be used inside `EvmData` impl and not in chains. - pub fn write_pointer(&mut self, data: Vec) { - let offset_position = self.data.len(); - H256::write(self, H256::repeat_byte(0xff)); - - self.offset_data.push(OffsetDatum { - offset_position, - data, - offset_shift: 0, - }); - } + /// Creates a new empty output builder (without selector). + pub fn new() -> Self { + Self { + data: vec![], + offset_data: vec![], + selector: None, + } + } + + /// Creates a new empty output builder with provided selector. + /// Selector will only be appended before the data when calling + /// `build` to not mess with the offsets. + pub fn new_with_selector(selector: impl Into) -> Self { + Self { + data: vec![], + offset_data: vec![], + selector: Some(selector.into()), + } + } + + /// Return the built data. + pub fn build(mut self) -> Vec { + Self::bake_offsets(&mut self.data, self.offset_data); + + if let Some(selector) = self.selector { + let mut output = selector.to_be_bytes().to_vec(); + output.append(&mut self.data); + output + } else { + self.data + } + } + + /// Add offseted data at the end of this writer's data, updating the offsets. + fn bake_offsets(output: &mut Vec, offsets: Vec) { + for mut offset_datum in offsets { + let offset_position = offset_datum.offset_position; + let offset_position_end = offset_position + 32; + + // The offset is the distance between the start of the data and the + // start of the pointed data (start of a struct, length of an array). + // Offsets in inner data are relative to the start of their respective "container". + // However in arrays the "container" is actually the item itself instead of the whole + // array, which is corrected by `offset_shift`. + let free_space_offset = output.len() - offset_datum.offset_shift; + + // Override dummy offset to the offset it will be in the final output. + U256::from(free_space_offset) + .to_big_endian(&mut output[offset_position..offset_position_end]); + + // Append this data at the end of the current output. + output.append(&mut offset_datum.data); + } + } + + /// Write arbitrary bytes. + /// Doesn't handle any alignement checks, prefer using `write` instead if possible. + fn write_raw_bytes(mut self, value: &[u8]) -> Self { + self.data.extend_from_slice(value); + self + } + + /// Write data of requested type. + pub fn write(mut self, value: T) -> Self { + T::write(&mut self, value); + self + } + + /// Writes a pointer to given data. + /// The data will be appended when calling `build`. + /// Initially write a dummy value as offset in this writer's data, which will be replaced by + /// the correct offset once the pointed data is appended. + /// + /// Takes `&mut self` since its goal is to be used inside `EvmData` impl and not in chains. + pub fn write_pointer(&mut self, data: Vec) { + let offset_position = self.data.len(); + H256::write(self, H256::repeat_byte(0xff)); + + self.offset_data.push(OffsetDatum { + offset_position, + data, + offset_shift: 0, + }); + } } impl Default for EvmDataWriter { - fn default() -> Self { - Self::new() - } + fn default() -> Self { + Self::new() + } } /// Data that can be converted from and to EVM data types. pub trait EvmData: Sized { - fn read(reader: &mut EvmDataReader) -> EvmResult; - fn write(writer: &mut EvmDataWriter, value: Self); - fn has_static_size() -> bool; + fn read(reader: &mut EvmDataReader) -> EvmResult; + fn write(writer: &mut EvmDataWriter, value: Self); + fn has_static_size() -> bool; } #[impl_for_tuples(1, 18)] impl EvmData for Tuple { - fn has_static_size() -> bool { - for_tuples!(#( Tuple::has_static_size() )&*) - } - - fn read(reader: &mut EvmDataReader) -> EvmResult { - if !Self::has_static_size() { - let reader = &mut reader.read_pointer()?; - Ok(for_tuples!( ( #( reader.read::()? ),* ) )) - } else { - Ok(for_tuples!( ( #( reader.read::()? ),* ) )) - } - } - - fn write(writer: &mut EvmDataWriter, value: Self) { - if !Self::has_static_size() { - let mut inner_writer = EvmDataWriter::new(); - for_tuples!( #( Tuple::write(&mut inner_writer, value.Tuple); )* ); - writer.write_pointer(inner_writer.build()); - } else { - for_tuples!( #( Tuple::write(writer, value.Tuple); )* ); - } - } + fn has_static_size() -> bool { + for_tuples!(#( Tuple::has_static_size() )&*) + } + + fn read(reader: &mut EvmDataReader) -> EvmResult { + if !Self::has_static_size() { + let reader = &mut reader.read_pointer()?; + Ok(for_tuples!( ( #( reader.read::()? ),* ) )) + } else { + Ok(for_tuples!( ( #( reader.read::()? ),* ) )) + } + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + if !Self::has_static_size() { + let mut inner_writer = EvmDataWriter::new(); + for_tuples!( #( Tuple::write(&mut inner_writer, value.Tuple); )* ); + writer.write_pointer(inner_writer.build()); + } else { + for_tuples!( #( Tuple::write(writer, value.Tuple); )* ); + } + } } impl EvmData for H256 { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let range = reader.move_cursor(32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; - let data = reader - .input - .get(range) - .ok_or_else(|| revert("tried to parse H256 out of bounds"))?; + let data = reader + .input + .get(range) + .ok_or_else(|| revert("tried to parse H256 out of bounds"))?; - Ok(H256::from_slice(data)) - } + Ok(H256::from_slice(data)) + } - fn write(writer: &mut EvmDataWriter, value: Self) { - writer.data.extend_from_slice(value.as_bytes()); - } + fn write(writer: &mut EvmDataWriter, value: Self) { + writer.data.extend_from_slice(value.as_bytes()); + } - fn has_static_size() -> bool { - true - } + fn has_static_size() -> bool { + true + } } impl EvmData for Address { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let range = reader.move_cursor(32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; - let data = reader - .input - .get(range) - .ok_or_else(|| revert("tried to parse H160 out of bounds"))?; + let data = reader + .input + .get(range) + .ok_or_else(|| revert("tried to parse H160 out of bounds"))?; - Ok(H160::from_slice(&data[12..32]).into()) - } + Ok(H160::from_slice(&data[12..32]).into()) + } - fn write(writer: &mut EvmDataWriter, value: Self) { - H256::write(writer, value.0.into()); - } + fn write(writer: &mut EvmDataWriter, value: Self) { + H256::write(writer, value.0.into()); + } - fn has_static_size() -> bool { - true - } + fn has_static_size() -> bool { + true + } } impl EvmData for U256 { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let range = reader.move_cursor(32)?; - - let data = reader - .input - .get(range) - .ok_or_else(|| revert("tried to parse U256 out of bounds"))?; - - Ok(U256::from_big_endian(data)) - } - - fn write(writer: &mut EvmDataWriter, value: Self) { - let mut buffer = [0u8; 32]; - value.to_big_endian(&mut buffer); - writer.data.extend_from_slice(&buffer); - } - - fn has_static_size() -> bool { - true - } + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; + + let data = reader + .input + .get(range) + .ok_or_else(|| revert("tried to parse U256 out of bounds"))?; + + Ok(U256::from_big_endian(data)) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut buffer = [0u8; 32]; + value.to_big_endian(&mut buffer); + writer.data.extend_from_slice(&buffer); + } + + fn has_static_size() -> bool { + true + } } macro_rules! impl_evmdata_for_uints { @@ -451,152 +451,152 @@ impl_evmdata_for_uints!(u16, u32, u64, u128,); // The implementation for u8 is specific, for performance reasons. impl EvmData for u8 { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let range = reader.move_cursor(32)?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let range = reader.move_cursor(32)?; - let data = reader - .input - .get(range) - .ok_or_else(|| revert("tried to parse u64 out of bounds"))?; + let data = reader + .input + .get(range) + .ok_or_else(|| revert("tried to parse u64 out of bounds"))?; - Ok(data[31]) - } + Ok(data[31]) + } - fn write(writer: &mut EvmDataWriter, value: Self) { - let mut buffer = [0u8; 32]; - buffer[31] = value; + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut buffer = [0u8; 32]; + buffer[31] = value; - writer.data.extend_from_slice(&buffer); - } + writer.data.extend_from_slice(&buffer); + } - fn has_static_size() -> bool { - true - } + fn has_static_size() -> bool { + true + } } impl EvmData for bool { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let h256 = H256::read(reader).map_err(|_| revert("tried to parse bool out of bounds"))?; + fn read(reader: &mut EvmDataReader) -> EvmResult { + let h256 = H256::read(reader).map_err(|_| revert("tried to parse bool out of bounds"))?; - Ok(!h256.is_zero()) - } + Ok(!h256.is_zero()) + } - fn write(writer: &mut EvmDataWriter, value: Self) { - let mut buffer = [0u8; 32]; - if value { - buffer[31] = 1; - } + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut buffer = [0u8; 32]; + if value { + buffer[31] = 1; + } - writer.data.extend_from_slice(&buffer); - } + writer.data.extend_from_slice(&buffer); + } - fn has_static_size() -> bool { - true - } + fn has_static_size() -> bool { + true + } } impl EvmData for Vec { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let mut inner_reader = reader.read_pointer()?; - - let array_size: usize = inner_reader - .read::() - .map_err(|_| revert("tried to parse array length out of bounds"))? - .try_into() - .map_err(|_| revert("array length is too large"))?; - - let mut array = vec![]; - - let mut item_reader = EvmDataReader { - input: inner_reader - .input - .get(32..) - .ok_or_else(|| revert("try to read array items out of bound"))?, - cursor: 0, - }; - - for _ in 0..array_size { - array.push(item_reader.read()?); - } - - Ok(array) - } - - fn write(writer: &mut EvmDataWriter, value: Self) { - let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len())); - - for inner in value { - // Any offset in items are relative to the start of the item instead of the - // start of the array. However if there is offseted data it must but appended after - // all items (offsets) are written. We thus need to rely on `compute_offsets` to do - // that, and must store a "shift" to correct the offsets. - let shift = inner_writer.data.len(); - let item_writer = EvmDataWriter::new().write(inner); - - inner_writer = inner_writer.write_raw_bytes(&item_writer.data); - for mut offset_datum in item_writer.offset_data { - offset_datum.offset_shift += 32; - offset_datum.offset_position += shift; - inner_writer.offset_data.push(offset_datum); - } - } - - writer.write_pointer(inner_writer.build()); - } - - fn has_static_size() -> bool { - false - } + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; + + let array_size: usize = inner_reader + .read::() + .map_err(|_| revert("tried to parse array length out of bounds"))? + .try_into() + .map_err(|_| revert("array length is too large"))?; + + let mut array = vec![]; + + let mut item_reader = EvmDataReader { + input: inner_reader + .input + .get(32..) + .ok_or_else(|| revert("try to read array items out of bound"))?, + cursor: 0, + }; + + for _ in 0..array_size { + array.push(item_reader.read()?); + } + + Ok(array) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let mut inner_writer = EvmDataWriter::new().write(U256::from(value.len())); + + for inner in value { + // Any offset in items are relative to the start of the item instead of the + // start of the array. However if there is offseted data it must but appended after + // all items (offsets) are written. We thus need to rely on `compute_offsets` to do + // that, and must store a "shift" to correct the offsets. + let shift = inner_writer.data.len(); + let item_writer = EvmDataWriter::new().write(inner); + + inner_writer = inner_writer.write_raw_bytes(&item_writer.data); + for mut offset_datum in item_writer.offset_data { + offset_datum.offset_shift += 32; + offset_datum.offset_position += shift; + inner_writer.offset_data.push(offset_datum); + } + } + + writer.write_pointer(inner_writer.build()); + } + + fn has_static_size() -> bool { + false + } } impl EvmData for Bytes { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let mut inner_reader = reader.read_pointer()?; - - // Read bytes/string size. - let array_size: usize = inner_reader - .read::() - .map_err(|_| revert("tried to parse bytes/string length out of bounds"))? - .try_into() - .map_err(|_| revert("bytes/string length is too large"))?; - - // Get valid range over the bytes data. - let range = inner_reader.move_cursor(array_size)?; - - let data = inner_reader - .input - .get(range) - .ok_or_else(|| revert("tried to parse bytes/string out of bounds"))?; - - let bytes = Self(data.to_owned()); - - Ok(bytes) - } - - fn write(writer: &mut EvmDataWriter, value: Self) { - let length = value.0.len(); - - // Pad the data. - // Leave it as is if a multiple of 32, otherwise pad to next - // multiple or 32. - let chunks = length / 32; - let padded_size = match length % 32 { - 0 => chunks * 32, - _ => (chunks + 1) * 32, - }; - - let mut value = value.0.to_vec(); - value.resize(padded_size, 0); - - writer.write_pointer( - EvmDataWriter::new() - .write(U256::from(length)) - .write_raw_bytes(&value) - .build(), - ); - } - - fn has_static_size() -> bool { - false - } + fn read(reader: &mut EvmDataReader) -> EvmResult { + let mut inner_reader = reader.read_pointer()?; + + // Read bytes/string size. + let array_size: usize = inner_reader + .read::() + .map_err(|_| revert("tried to parse bytes/string length out of bounds"))? + .try_into() + .map_err(|_| revert("bytes/string length is too large"))?; + + // Get valid range over the bytes data. + let range = inner_reader.move_cursor(array_size)?; + + let data = inner_reader + .input + .get(range) + .ok_or_else(|| revert("tried to parse bytes/string out of bounds"))?; + + let bytes = Self(data.to_owned()); + + Ok(bytes) + } + + fn write(writer: &mut EvmDataWriter, value: Self) { + let length = value.0.len(); + + // Pad the data. + // Leave it as is if a multiple of 32, otherwise pad to next + // multiple or 32. + let chunks = length / 32; + let padded_size = match length % 32 { + 0 => chunks * 32, + _ => (chunks + 1) * 32, + }; + + let mut value = value.0.to_vec(); + value.resize(padded_size, 0); + + writer.write_pointer( + EvmDataWriter::new() + .write(U256::from(length)) + .write_raw_bytes(&value) + .build(), + ); + } + + fn has_static_size() -> bool { + false + } } diff --git a/runtime/common/assets-evm-utils/src/lib.rs b/runtime/common/assets-evm-utils/src/lib.rs index a1648caa..289b4b73 100644 --- a/runtime/common/assets-evm-utils/src/lib.rs +++ b/runtime/common/assets-evm-utils/src/lib.rs @@ -23,12 +23,12 @@ extern crate alloc; use crate::alloc::borrow::ToOwned; use fp_evm::{ - Context, ExitError, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, - PrecompileOutput, + Context, ExitError, ExitRevert, ExitSucceed, PrecompileFailure, PrecompileHandle, + PrecompileOutput, }; use frame_support::{ - dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, - traits::Get, + dispatch::{Dispatchable, GetDispatchInfo, PostDispatchInfo}, + traits::Get, }; use pallet_evm::{GasWeightMapping, Log}; use sp_core::{H160, H256, U256}; @@ -36,11 +36,9 @@ use sp_std::{marker::PhantomData, vec, vec::Vec}; mod data; -pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; pub use assets_macro::{generate_function_selector, keccak256}; +pub use data::{Address, Bytes, EvmData, EvmDataReader, EvmDataWriter}; -#[cfg(feature = "testing")] -pub mod testing; #[cfg(test)] mod tests; @@ -52,109 +50,109 @@ pub type EvmResult = Result; /// consumed all the gas limit and the error message is not easily /// retrievable. pub fn error>>(text: T) -> PrecompileFailure { - PrecompileFailure::Error { - exit_status: ExitError::Other(text.into()), - } + PrecompileFailure::Error { + exit_status: ExitError::Other(text.into()), + } } /// Builder for PrecompileOutput. #[derive(Clone, Debug)] pub struct LogsBuilder { - address: H160, + address: H160, } impl LogsBuilder { - /// Create a new builder with no logs. - /// Takes the address of the precompile (usually `context.address`). - pub fn new(address: H160) -> Self { - Self { address } - } - - /// Create a 0-topic log. - #[must_use] - pub fn log0(&self, data: impl Into>) -> Log { - Log { - address: self.address, - topics: vec![], - data: data.into(), - } - } - - /// Create a 1-topic log. - #[must_use] - pub fn log1(&self, topic0: impl Into, data: impl Into>) -> Log { - Log { - address: self.address, - topics: vec![topic0.into()], - data: data.into(), - } - } - - /// Create a 2-topics log. - #[must_use] - pub fn log2( - &self, - topic0: impl Into, - topic1: impl Into, - data: impl Into>, - ) -> Log { - Log { - address: self.address, - topics: vec![topic0.into(), topic1.into()], - data: data.into(), - } - } - - /// Create a 3-topics log. - #[must_use] - pub fn log3( - &self, - topic0: impl Into, - topic1: impl Into, - topic2: impl Into, - data: impl Into>, - ) -> Log { - Log { - address: self.address, - topics: vec![topic0.into(), topic1.into(), topic2.into()], - data: data.into(), - } - } - - /// Create a 4-topics log. - #[must_use] - pub fn log4( - &self, - topic0: impl Into, - topic1: impl Into, - topic2: impl Into, - topic3: impl Into, - data: impl Into>, - ) -> Log { - Log { - address: self.address, - topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()], - data: data.into(), - } - } + /// Create a new builder with no logs. + /// Takes the address of the precompile (usually `context.address`). + pub fn new(address: H160) -> Self { + Self { address } + } + + /// Create a 0-topic log. + #[must_use] + pub fn log0(&self, data: impl Into>) -> Log { + Log { + address: self.address, + topics: vec![], + data: data.into(), + } + } + + /// Create a 1-topic log. + #[must_use] + pub fn log1(&self, topic0: impl Into, data: impl Into>) -> Log { + Log { + address: self.address, + topics: vec![topic0.into()], + data: data.into(), + } + } + + /// Create a 2-topics log. + #[must_use] + pub fn log2( + &self, + topic0: impl Into, + topic1: impl Into, + data: impl Into>, + ) -> Log { + Log { + address: self.address, + topics: vec![topic0.into(), topic1.into()], + data: data.into(), + } + } + + /// Create a 3-topics log. + #[must_use] + pub fn log3( + &self, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + data: impl Into>, + ) -> Log { + Log { + address: self.address, + topics: vec![topic0.into(), topic1.into(), topic2.into()], + data: data.into(), + } + } + + /// Create a 4-topics log. + #[must_use] + pub fn log4( + &self, + topic0: impl Into, + topic1: impl Into, + topic2: impl Into, + topic3: impl Into, + data: impl Into>, + ) -> Log { + Log { + address: self.address, + topics: vec![topic0.into(), topic1.into(), topic2.into(), topic3.into()], + data: data.into(), + } + } } /// Extension trait allowing to record logs into a PrecompileHandle. pub trait LogExt { - fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult; + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult; - fn compute_cost(&self) -> EvmResult; + fn compute_cost(&self) -> EvmResult; } impl LogExt for Log { - fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult { - handle.log(self.address, self.topics, self.data)?; - Ok(()) - } - - fn compute_cost(&self) -> EvmResult { - log_costs(self.topics.len(), self.data.len()) - } + fn record(self, handle: &mut impl PrecompileHandle) -> EvmResult { + handle.log(self.address, self.topics, self.data)?; + Ok(()) + } + + fn compute_cost(&self) -> EvmResult { + log_costs(self.topics.len(), self.data.len()) + } } /// Helper functions requiring a Runtime. @@ -164,213 +162,213 @@ pub struct RuntimeHelper(PhantomData); impl RuntimeHelper where - Runtime: pallet_evm::Config, - Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, + Runtime: pallet_evm::Config, + Runtime::RuntimeCall: Dispatchable + GetDispatchInfo, { - /// Try to dispatch a Substrate call. - /// Return an error if there are not enough gas, or if the call fails. - /// If successful returns the used gas using the Runtime GasWeightMapping. - pub fn try_dispatch( - handle: &mut impl PrecompileHandleExt, - origin: ::RuntimeOrigin, - call: Call, - ) -> EvmResult<()> - where - Runtime::RuntimeCall: From, - { - let call = Runtime::RuntimeCall::from(call); - let dispatch_info = call.get_dispatch_info(); - - // Make sure there is enough gas. - let remaining_gas = handle.remaining_gas(); - let required_gas = Runtime::GasWeightMapping::weight_to_gas(dispatch_info.weight); - if required_gas > remaining_gas { - return Err(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }); - } - - // Dispatch call. - // It may be possible to not record gas cost if the call returns Pays::No. - // However while Substrate handle checking weight while not making the sender pay for it, - // the EVM doesn't. It seems this safer to always record the costs to avoid unmetered - // computations. - let used_weight = call - .dispatch(origin) - .map_err(|e| revert(alloc::format!("Dispatched call failed with error: {:?}", e)))? - .actual_weight; - - let used_gas = - Runtime::GasWeightMapping::weight_to_gas(used_weight.unwrap_or(dispatch_info.weight)); - - handle.record_cost(used_gas)?; - - Ok(()) - } + /// Try to dispatch a Substrate call. + /// Return an error if there are not enough gas, or if the call fails. + /// If successful returns the used gas using the Runtime GasWeightMapping. + pub fn try_dispatch( + handle: &mut impl PrecompileHandleExt, + origin: ::RuntimeOrigin, + call: Call, + ) -> EvmResult<()> + where + Runtime::RuntimeCall: From, + { + let call = Runtime::RuntimeCall::from(call); + let dispatch_info = call.get_dispatch_info(); + + // Make sure there is enough gas. + let remaining_gas = handle.remaining_gas(); + let required_gas = Runtime::GasWeightMapping::weight_to_gas(dispatch_info.weight); + if required_gas > remaining_gas { + return Err(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }); + } + + // Dispatch call. + // It may be possible to not record gas cost if the call returns Pays::No. + // However while Substrate handle checking weight while not making the sender pay for it, + // the EVM doesn't. It seems this safer to always record the costs to avoid unmetered + // computations. + let used_weight = call + .dispatch(origin) + .map_err(|e| revert(alloc::format!("Dispatched call failed with error: {:?}", e)))? + .actual_weight; + + let used_gas = + Runtime::GasWeightMapping::weight_to_gas(used_weight.unwrap_or(dispatch_info.weight)); + + handle.record_cost(used_gas)?; + + Ok(()) + } } impl RuntimeHelper where - Runtime: pallet_evm::Config, + Runtime: pallet_evm::Config, { - /// Cost of a Substrate DB write in gas. - pub fn db_write_gas_cost() -> u64 { - ::GasWeightMapping::weight_to_gas( - ::DbWeight::get().writes(1), - ) - } - - /// Cost of a Substrate DB read in gas. - pub fn db_read_gas_cost() -> u64 { - ::GasWeightMapping::weight_to_gas( - ::DbWeight::get().reads(1), - ) - } + /// Cost of a Substrate DB write in gas. + pub fn db_write_gas_cost() -> u64 { + ::GasWeightMapping::weight_to_gas( + ::DbWeight::get().writes(1), + ) + } + + /// Cost of a Substrate DB read in gas. + pub fn db_read_gas_cost() -> u64 { + ::GasWeightMapping::weight_to_gas( + ::DbWeight::get().reads(1), + ) + } } /// Represents modifiers a Solidity function can be annotated with. #[derive(Copy, Clone, PartialEq, Eq)] pub enum FunctionModifier { - /// Function that doesn't modify the state. - View, - /// Function that modifies the state but refuse receiving funds. - /// Correspond to a Solidity function with no modifiers. - NonPayable, - /// Function that modifies the state and accept funds. - Payable, + /// Function that doesn't modify the state. + View, + /// Function that modifies the state but refuse receiving funds. + /// Correspond to a Solidity function with no modifiers. + NonPayable, + /// Function that modifies the state and accept funds. + Payable, } pub trait PrecompileHandleExt: PrecompileHandle { - #[must_use] - /// Record cost of a log manually. - /// This can be useful to record log costs early when their content have static size. - fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult; - - #[must_use] - /// Record cost of logs. - fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult; - - #[must_use] - /// Check that a function call is compatible with the context it is - /// called into. - fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult; - - #[must_use] - /// Read the selector from the input data. - fn read_selector(&self) -> EvmResult - where - T: num_enum::TryFromPrimitive; - - #[must_use] - /// Returns a reader of the input, skipping the selector. - fn read_input(&self) -> EvmResult; + #[must_use] + /// Record cost of a log manually. + /// This can be useful to record log costs early when their content have static size. + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult; + + #[must_use] + /// Record cost of logs. + fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult; + + #[must_use] + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult; + + #[must_use] + /// Read the selector from the input data. + fn read_selector(&self) -> EvmResult + where + T: num_enum::TryFromPrimitive; + + #[must_use] + /// Returns a reader of the input, skipping the selector. + fn read_input(&self) -> EvmResult; } pub fn log_costs(topics: usize, data_len: usize) -> EvmResult { - // Cost calculation is copied from EVM code that is not publicly exposed by the crates. - // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 - - const G_LOG: u64 = 375; - const G_LOGDATA: u64 = 8; - const G_LOGTOPIC: u64 = 375; - - let topic_cost = G_LOGTOPIC - .checked_mul(topics as u64) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - })?; - - let data_cost = G_LOGDATA - .checked_mul(data_len as u64) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - })?; - - G_LOG - .checked_add(topic_cost) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - })? - .checked_add(data_cost) - .ok_or(PrecompileFailure::Error { - exit_status: ExitError::OutOfGas, - }) + // Cost calculation is copied from EVM code that is not publicly exposed by the crates. + // https://github.com/rust-blockchain/evm/blob/master/gasometer/src/costs.rs#L148 + + const G_LOG: u64 = 375; + const G_LOGDATA: u64 = 8; + const G_LOGTOPIC: u64 = 375; + + let topic_cost = G_LOGTOPIC + .checked_mul(topics as u64) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; + + let data_cost = G_LOGDATA + .checked_mul(data_len as u64) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; + + G_LOG + .checked_add(topic_cost) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })? + .checked_add(data_cost) + .ok_or(PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + }) } impl PrecompileHandleExt for T { - #[must_use] - /// Record cost of a log manualy. - /// This can be useful to record log costs early when their content have static size. - fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { - self.record_cost(log_costs(topics, data_len)?)?; - - Ok(()) - } - - #[must_use] - /// Record cost of logs. - fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult { - for log in logs { - self.record_log_costs_manual(log.topics.len(), log.data.len())?; - } - - Ok(()) - } - - #[must_use] - /// Check that a function call is compatible with the context it is - /// called into. - fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult { - check_function_modifier(self.context(), self.is_static(), modifier) - } - - #[must_use] - /// Read the selector from the input data. - fn read_selector(&self) -> EvmResult - where - S: num_enum::TryFromPrimitive, - { - EvmDataReader::read_selector(self.input()) - } - - #[must_use] - /// Returns a reader of the input, skipping the selector. - fn read_input(&self) -> EvmResult { - EvmDataReader::new_skip_selector(self.input()) - } + #[must_use] + /// Record cost of a log manualy. + /// This can be useful to record log costs early when their content have static size. + fn record_log_costs_manual(&mut self, topics: usize, data_len: usize) -> EvmResult { + self.record_cost(log_costs(topics, data_len)?)?; + + Ok(()) + } + + #[must_use] + /// Record cost of logs. + fn record_log_costs(&mut self, logs: &[&Log]) -> EvmResult { + for log in logs { + self.record_log_costs_manual(log.topics.len(), log.data.len())?; + } + + Ok(()) + } + + #[must_use] + /// Check that a function call is compatible with the context it is + /// called into. + fn check_function_modifier(&self, modifier: FunctionModifier) -> EvmResult { + check_function_modifier(self.context(), self.is_static(), modifier) + } + + #[must_use] + /// Read the selector from the input data. + fn read_selector(&self) -> EvmResult + where + S: num_enum::TryFromPrimitive, + { + EvmDataReader::read_selector(self.input()) + } + + #[must_use] + /// Returns a reader of the input, skipping the selector. + fn read_input(&self) -> EvmResult { + EvmDataReader::new_skip_selector(self.input()) + } } #[must_use] pub fn revert(output: impl AsRef<[u8]>) -> PrecompileFailure { - PrecompileFailure::Revert { - exit_status: ExitRevert::Reverted, - output: output.as_ref().to_owned(), - } + PrecompileFailure::Revert { + exit_status: ExitRevert::Reverted, + output: output.as_ref().to_owned(), + } } #[must_use] pub fn succeed(output: impl AsRef<[u8]>) -> PrecompileOutput { - PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: output.as_ref().to_owned(), - } + PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: output.as_ref().to_owned(), + } } #[must_use] /// Check that a function call is compatible with the context it is /// called into. fn check_function_modifier( - context: &Context, - is_static: bool, - modifier: FunctionModifier, + context: &Context, + is_static: bool, + modifier: FunctionModifier, ) -> EvmResult { - if is_static && modifier != FunctionModifier::View { - return Err(revert("can't call non-static function in static context")); - } + if is_static && modifier != FunctionModifier::View { + return Err(revert("can't call non-static function in static context")); + } - if modifier != FunctionModifier::Payable && context.apparent_value > U256::zero() { - return Err(revert("function is not payable")); - } + if modifier != FunctionModifier::Payable && context.apparent_value > U256::zero() { + return Err(revert("function is not payable")); + } - Ok(()) + Ok(()) } diff --git a/runtime/common/assets-evm-utils/src/tests.rs b/runtime/common/assets-evm-utils/src/tests.rs index 7b8b9ad2..0ad7dd57 100644 --- a/runtime/common/assets-evm-utils/src/tests.rs +++ b/runtime/common/assets-evm-utils/src/tests.rs @@ -21,618 +21,618 @@ use hex_literal::hex; use sp_core::{H256, U256}; fn u256_repeat_byte(byte: u8) -> U256 { - let value = H256::repeat_byte(byte); + let value = H256::repeat_byte(byte); - U256::from_big_endian(value.as_bytes()) + U256::from_big_endian(value.as_bytes()) } // When debugging it is useful to display data in chunks of 32 bytes. #[allow(dead_code)] fn display_bytes(bytes: &[u8]) { - bytes - .chunks_exact(32) - .map(|chunk| H256::from_slice(chunk)) - .for_each(|hash| println!("{:?}", hash)); + bytes + .chunks_exact(32) + .map(|chunk| H256::from_slice(chunk)) + .for_each(|hash| println!("{:?}", hash)); } #[test] fn write_bool() { - let value = true; + let value = true; - let writer_output = EvmDataWriter::new().write(value).build(); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut expected_output = [0u8; 32]; - expected_output[31] = 1; + let mut expected_output = [0u8; 32]; + expected_output[31] = 1; - assert_eq!(writer_output, expected_output); + assert_eq!(writer_output, expected_output); } #[test] fn read_bool() { - let value = true; + let value = true; - let writer_output = EvmDataWriter::new().write(value).build(); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: bool = reader.read().expect("to correctly parse bool"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: bool = reader.read().expect("to correctly parse bool"); - assert_eq!(value, parsed); + assert_eq!(value, parsed); } #[test] fn write_u64() { - let value = 42u64; + let value = 42u64; - let writer_output = EvmDataWriter::new().write(value).build(); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut expected_output = [0u8; 32]; - expected_output[24..].copy_from_slice(&value.to_be_bytes()); + let mut expected_output = [0u8; 32]; + expected_output[24..].copy_from_slice(&value.to_be_bytes()); - assert_eq!(writer_output, expected_output); + assert_eq!(writer_output, expected_output); } #[test] fn read_u64() { - let value = 42u64; - let writer_output = EvmDataWriter::new().write(value).build(); + let value = 42u64; + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: u64 = reader.read().expect("to correctly parse u64"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: u64 = reader.read().expect("to correctly parse u64"); - assert_eq!(value, parsed); + assert_eq!(value, parsed); } #[test] fn write_u128() { - let value = 42u128; + let value = 42u128; - let writer_output = EvmDataWriter::new().write(value).build(); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut expected_output = [0u8; 32]; - expected_output[16..].copy_from_slice(&value.to_be_bytes()); + let mut expected_output = [0u8; 32]; + expected_output[16..].copy_from_slice(&value.to_be_bytes()); - assert_eq!(writer_output, expected_output); + assert_eq!(writer_output, expected_output); } #[test] fn read_u128() { - let value = 42u128; - let writer_output = EvmDataWriter::new().write(value).build(); + let value = 42u128; + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: u128 = reader.read().expect("to correctly parse u128"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: u128 = reader.read().expect("to correctly parse u128"); - assert_eq!(value, parsed); + assert_eq!(value, parsed); } #[test] fn write_u256() { - let value = U256::from(42); + let value = U256::from(42); - let writer_output = EvmDataWriter::new().write(value).build(); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut expected_output = [0u8; 32]; - value.to_big_endian(&mut expected_output); + let mut expected_output = [0u8; 32]; + value.to_big_endian(&mut expected_output); - assert_eq!(writer_output, expected_output); + assert_eq!(writer_output, expected_output); } #[test] fn read_u256() { - let value = U256::from(42); - let writer_output = EvmDataWriter::new().write(value).build(); + let value = U256::from(42); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: U256 = reader.read().expect("to correctly parse U256"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: U256 = reader.read().expect("to correctly parse U256"); - assert_eq!(value, parsed); + assert_eq!(value, parsed); } #[test] fn read_selector() { - use sha3::{Digest, Keccak256}; + use sha3::{Digest, Keccak256}; - #[assets_macro::generate_function_selector] - #[derive(Debug, PartialEq)] - enum FakeAction { - Action1 = "action1()", - } + #[assets_macro::generate_function_selector] + #[derive(Debug, PartialEq)] + enum FakeAction { + Action1 = "action1()", + } - let selector = &Keccak256::digest(b"action1()")[0..4]; + let selector = &Keccak256::digest(b"action1()")[0..4]; - let parsed_selector = - EvmDataReader::read_selector::(selector).expect("there is a selector"); - EvmDataReader::new_skip_selector(selector).expect("there is a selector"); + let parsed_selector = + EvmDataReader::read_selector::(selector).expect("there is a selector"); + EvmDataReader::new_skip_selector(selector).expect("there is a selector"); - assert_eq!(parsed_selector, FakeAction::Action1) + assert_eq!(parsed_selector, FakeAction::Action1) } #[test] #[should_panic(expected = "to correctly parse U256")] fn read_u256_too_short() { - let value = U256::from(42); - let writer_output = EvmDataWriter::new().write(value).build(); + let value = U256::from(42); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output[0..31]); - let _: U256 = reader.read().expect("to correctly parse U256"); + let mut reader = EvmDataReader::new(&writer_output[0..31]); + let _: U256 = reader.read().expect("to correctly parse U256"); } #[test] fn write_h256() { - let mut raw = [0u8; 32]; - raw[0] = 42; - raw[12] = 43; - raw[31] = 44; + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; - let value = H256::from(raw); + let value = H256::from(raw); - let output = EvmDataWriter::new().write(value).build(); + let output = EvmDataWriter::new().write(value).build(); - assert_eq!(&output, &raw); + assert_eq!(&output, &raw); } #[test] fn tmp() { - let u = U256::from(1_000_000_000); - println!("U256={:?}", u.0); + let u = U256::from(1_000_000_000); + println!("U256={:?}", u.0); } #[test] fn read_h256() { - let mut raw = [0u8; 32]; - raw[0] = 42; - raw[12] = 43; - raw[31] = 44; - let value = H256::from(raw); - let writer_output = EvmDataWriter::new().write(value).build(); + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; + let value = H256::from(raw); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: H256 = reader.read().expect("to correctly parse H256"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: H256 = reader.read().expect("to correctly parse H256"); - assert_eq!(value, parsed); + assert_eq!(value, parsed); } #[test] #[should_panic(expected = "to correctly parse H256")] fn read_h256_too_short() { - let mut raw = [0u8; 32]; - raw[0] = 42; - raw[12] = 43; - raw[31] = 44; - let value = H256::from(raw); - let writer_output = EvmDataWriter::new().write(value).build(); + let mut raw = [0u8; 32]; + raw[0] = 42; + raw[12] = 43; + raw[31] = 44; + let value = H256::from(raw); + let writer_output = EvmDataWriter::new().write(value).build(); - let mut reader = EvmDataReader::new(&writer_output[0..31]); - let _: H256 = reader.read().expect("to correctly parse H256"); + let mut reader = EvmDataReader::new(&writer_output[0..31]); + let _: H256 = reader.read().expect("to correctly parse H256"); } #[test] fn write_address() { - let value = H160::repeat_byte(0xAA); + let value = H160::repeat_byte(0xAA); - let output = EvmDataWriter::new().write(Address(value)).build(); + let output = EvmDataWriter::new().write(Address(value)).build(); - assert_eq!(output.len(), 32); - assert_eq!(&output[12..32], value.as_bytes()); + assert_eq!(output.len(), 32); + assert_eq!(&output[12..32], value.as_bytes()); } #[test] fn read_address() { - let value = H160::repeat_byte(0xAA); - let writer_output = EvmDataWriter::new().write(Address(value)).build(); + let value = H160::repeat_byte(0xAA); + let writer_output = EvmDataWriter::new().write(Address(value)).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Address = reader.read().expect("to correctly parse Address"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Address = reader.read().expect("to correctly parse Address"); - assert_eq!(value, parsed.0); + assert_eq!(value, parsed.0); } #[test] fn write_h256_array() { - let array = vec![ - H256::repeat_byte(0x11), - H256::repeat_byte(0x22), - H256::repeat_byte(0x33), - H256::repeat_byte(0x44), - H256::repeat_byte(0x55), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); - assert_eq!(writer_output.len(), 0xE0); + let array = vec![ + H256::repeat_byte(0x11), + H256::repeat_byte(0x22), + H256::repeat_byte(0x33), + H256::repeat_byte(0x44), + H256::repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0xE0); - // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::().expect("read offset"), 32.into()); - assert_eq!(reader.read::().expect("read size"), 5.into()); - assert_eq!(reader.read::().expect("read 1st"), array[0]); - assert_eq!(reader.read::().expect("read 2nd"), array[1]); - assert_eq!(reader.read::().expect("read 3rd"), array[2]); - assert_eq!(reader.read::().expect("read 4th"), array[3]); - assert_eq!(reader.read::().expect("read 5th"), array[4]); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); } #[test] fn read_h256_array() { - let array = vec![ - H256::repeat_byte(0x11), - H256::repeat_byte(0x22), - H256::repeat_byte(0x33), - H256::repeat_byte(0x44), - H256::repeat_byte(0x55), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); + let array = vec![ + H256::repeat_byte(0x11), + H256::repeat_byte(0x22), + H256::repeat_byte(0x33), + H256::repeat_byte(0x44), + H256::repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec = reader.read().expect("to correctly parse Vec"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); - assert_eq!(array, parsed); + assert_eq!(array, parsed); } #[test] fn write_u256_array() { - let array = vec![ - u256_repeat_byte(0x11), - u256_repeat_byte(0x22), - u256_repeat_byte(0x33), - u256_repeat_byte(0x44), - u256_repeat_byte(0x55), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); - assert_eq!(writer_output.len(), 0xE0); + let array = vec![ + u256_repeat_byte(0x11), + u256_repeat_byte(0x22), + u256_repeat_byte(0x33), + u256_repeat_byte(0x44), + u256_repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0xE0); - // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::().expect("read offset"), 32.into()); - assert_eq!(reader.read::().expect("read size"), 5.into()); - assert_eq!(reader.read::().expect("read 1st"), array[0]); - assert_eq!(reader.read::().expect("read 2nd"), array[1]); - assert_eq!(reader.read::().expect("read 3rd"), array[2]); - assert_eq!(reader.read::().expect("read 4th"), array[3]); - assert_eq!(reader.read::().expect("read 5th"), array[4]); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::().expect("read 1st"), array[0]); + assert_eq!(reader.read::().expect("read 2nd"), array[1]); + assert_eq!(reader.read::().expect("read 3rd"), array[2]); + assert_eq!(reader.read::().expect("read 4th"), array[3]); + assert_eq!(reader.read::().expect("read 5th"), array[4]); } #[test] fn read_u256_array() { - let array = vec![ - u256_repeat_byte(0x11), - u256_repeat_byte(0x22), - u256_repeat_byte(0x33), - u256_repeat_byte(0x44), - u256_repeat_byte(0x55), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); + let array = vec![ + u256_repeat_byte(0x11), + u256_repeat_byte(0x22), + u256_repeat_byte(0x33), + u256_repeat_byte(0x44), + u256_repeat_byte(0x55), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec = reader.read().expect("to correctly parse Vec"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); - assert_eq!(array, parsed); + assert_eq!(array, parsed); } #[test] fn write_address_array() { - let array = vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - Address(H160::repeat_byte(0x44)), - Address(H160::repeat_byte(0x55)), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); - // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::().expect("read offset"), 32.into()); - assert_eq!(reader.read::().expect("read size"), 5.into()); - assert_eq!(reader.read::
().expect("read 1st"), array[0]); - assert_eq!(reader.read::
().expect("read 2nd"), array[1]); - assert_eq!(reader.read::
().expect("read 3rd"), array[2]); - assert_eq!(reader.read::
().expect("read 4th"), array[3]); - assert_eq!(reader.read::
().expect("read 5th"), array[4]); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), 5.into()); + assert_eq!(reader.read::
().expect("read 1st"), array[0]); + assert_eq!(reader.read::
().expect("read 2nd"), array[1]); + assert_eq!(reader.read::
().expect("read 3rd"), array[2]); + assert_eq!(reader.read::
().expect("read 4th"), array[3]); + assert_eq!(reader.read::
().expect("read 5th"), array[4]); } #[test] fn read_address_array() { - let array = vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - Address(H160::repeat_byte(0x44)), - Address(H160::repeat_byte(0x55)), - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec
= reader.read().expect("to correctly parse Vec"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec
= reader.read().expect("to correctly parse Vec"); - assert_eq!(array, parsed); + assert_eq!(array, parsed); } #[test] fn read_address_array_size_too_big() { - let array = vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - Address(H160::repeat_byte(0x44)), - Address(H160::repeat_byte(0x55)), - ]; - let mut writer_output = EvmDataWriter::new().write(array).build(); + let array = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ]; + let mut writer_output = EvmDataWriter::new().write(array).build(); - U256::from(6u32).to_big_endian(&mut writer_output[0x20..0x40]); + U256::from(6u32).to_big_endian(&mut writer_output[0x20..0x40]); - let mut reader = EvmDataReader::new(&writer_output); + let mut reader = EvmDataReader::new(&writer_output); - match reader.read::>() { - Ok(_) => panic!("should not parse correctly"), - Err(PrecompileFailure::Revert { output: err, .. }) => { - assert_eq!(err, b"tried to parse H160 out of bounds") - } - Err(_) => panic!("unexpected error"), - } + match reader.read::>() { + Ok(_) => panic!("should not parse correctly"), + Err(PrecompileFailure::Revert { output: err, .. }) => { + assert_eq!(err, b"tried to parse H160 out of bounds") + } + Err(_) => panic!("unexpected error"), + } } #[test] fn write_address_nested_array() { - let array = vec![ - vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - ], - vec![ - Address(H160::repeat_byte(0x44)), - Address(H160::repeat_byte(0x55)), - ], - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); - assert_eq!(writer_output.len(), 0x160); - - // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut reader = EvmDataReader::new(&writer_output); - - assert_eq!(reader.read::().expect("read offset"), 0x20.into()); // 0x00 - assert_eq!(reader.read::().expect("read size"), 2.into()); // 0x20 - assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x40 - assert_eq!(reader.read::().expect("read 2st offset"), 0xc0.into()); // 0x60 - assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x80 - assert_eq!(reader.read::
().expect("read 1-1"), array[0][0]); // 0xA0 - assert_eq!(reader.read::
().expect("read 1-2"), array[0][1]); // 0xC0 - assert_eq!(reader.read::
().expect("read 1-3"), array[0][2]); // 0xE0 - assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0x100 - assert_eq!(reader.read::
().expect("read 2-1"), array[1][0]); // 0x120 - assert_eq!(reader.read::
().expect("read 2-2"), array[1][1]); // 0x140 + let array = vec![ + vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ], + vec![ + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ], + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); + assert_eq!(writer_output.len(), 0x160); + + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); + + assert_eq!(reader.read::().expect("read offset"), 0x20.into()); // 0x00 + assert_eq!(reader.read::().expect("read size"), 2.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x40 + assert_eq!(reader.read::().expect("read 2st offset"), 0xc0.into()); // 0x60 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x80 + assert_eq!(reader.read::
().expect("read 1-1"), array[0][0]); // 0xA0 + assert_eq!(reader.read::
().expect("read 1-2"), array[0][1]); // 0xC0 + assert_eq!(reader.read::
().expect("read 1-3"), array[0][2]); // 0xE0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0x100 + assert_eq!(reader.read::
().expect("read 2-1"), array[1][0]); // 0x120 + assert_eq!(reader.read::
().expect("read 2-2"), array[1][1]); // 0x140 } #[test] fn read_address_nested_array() { - let array = vec![ - vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - ], - vec![ - Address(H160::repeat_byte(0x44)), - Address(H160::repeat_byte(0x55)), - ], - ]; - let writer_output = EvmDataWriter::new().write(array.clone()).build(); + let array = vec![ + vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ], + vec![ + Address(H160::repeat_byte(0x44)), + Address(H160::repeat_byte(0x55)), + ], + ]; + let writer_output = EvmDataWriter::new().write(array.clone()).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec> = reader.read().expect("to correctly parse Vec>"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec> = reader.read().expect("to correctly parse Vec>"); - assert_eq!(array, parsed); + assert_eq!(array, parsed); } #[test] fn write_multiple_arrays() { - let array1 = vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - ]; + let array1 = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ]; - let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; + let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; - let writer_output = EvmDataWriter::new() - .write(array1.clone()) - .write(array2.clone()) - .build(); + let writer_output = EvmDataWriter::new() + .write(array1.clone()) + .write(array2.clone()) + .build(); - assert_eq!(writer_output.len(), 0x120); + assert_eq!(writer_output.len(), 0x120); - // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions since arrays are 32-byte aligned. + let mut reader = EvmDataReader::new(&writer_output); - assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x00 - assert_eq!(reader.read::().expect("read 2nd offset"), 0xc0.into()); // 0x20 - assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x40 - assert_eq!(reader.read::
().expect("read 1-1"), array1[0]); // 0x60 - assert_eq!(reader.read::
().expect("read 1-2"), array1[1]); // 0x80 - assert_eq!(reader.read::
().expect("read 1-3"), array1[2]); // 0xA0 - assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0xC0 - assert_eq!(reader.read::().expect("read 2-1"), array2[0]); // 0xE0 - assert_eq!(reader.read::().expect("read 2-2"), array2[1]); // 0x100 + assert_eq!(reader.read::().expect("read 1st offset"), 0x40.into()); // 0x00 + assert_eq!(reader.read::().expect("read 2nd offset"), 0xc0.into()); // 0x20 + assert_eq!(reader.read::().expect("read 1st size"), 3.into()); // 0x40 + assert_eq!(reader.read::
().expect("read 1-1"), array1[0]); // 0x60 + assert_eq!(reader.read::
().expect("read 1-2"), array1[1]); // 0x80 + assert_eq!(reader.read::
().expect("read 1-3"), array1[2]); // 0xA0 + assert_eq!(reader.read::().expect("read 2nd size"), 2.into()); // 0xC0 + assert_eq!(reader.read::().expect("read 2-1"), array2[0]); // 0xE0 + assert_eq!(reader.read::().expect("read 2-2"), array2[1]); // 0x100 } #[test] fn read_multiple_arrays() { - let array1 = vec![ - Address(H160::repeat_byte(0x11)), - Address(H160::repeat_byte(0x22)), - Address(H160::repeat_byte(0x33)), - ]; + let array1 = vec![ + Address(H160::repeat_byte(0x11)), + Address(H160::repeat_byte(0x22)), + Address(H160::repeat_byte(0x33)), + ]; - let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; + let array2 = vec![H256::repeat_byte(0x44), H256::repeat_byte(0x55)]; - let writer_output = EvmDataWriter::new() - .write(array1.clone()) - .write(array2.clone()) - .build(); + let writer_output = EvmDataWriter::new() + .write(array1.clone()) + .write(array2.clone()) + .build(); - // offset 0x20 - // offset 0x40 - // size 0x60 - // 3 addresses 0xC0 - // size 0xE0 - // 2 H256 0x120 - assert_eq!(writer_output.len(), 0x120); + // offset 0x20 + // offset 0x40 + // size 0x60 + // 3 addresses 0xC0 + // size 0xE0 + // 2 H256 0x120 + assert_eq!(writer_output.len(), 0x120); - let mut reader = EvmDataReader::new(&writer_output); + let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec
= reader.read().expect("to correctly parse Vec
"); - assert_eq!(array1, parsed); + let parsed: Vec
= reader.read().expect("to correctly parse Vec
"); + assert_eq!(array1, parsed); - let parsed: Vec = reader.read().expect("to correctly parse Vec"); - assert_eq!(array2, parsed); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); + assert_eq!(array2, parsed); } #[test] fn read_bytes() { - let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new().write(Bytes::from(&data[..])).build(); + let writer_output = EvmDataWriter::new().write(Bytes::from(&data[..])).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); - assert_eq!(data, parsed.as_bytes()); + assert_eq!(data, parsed.as_bytes()); } #[test] fn write_bytes() { - let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new().write(Bytes::from(&data[..])).build(); + let writer_output = EvmDataWriter::new().write(Bytes::from(&data[..])).build(); - // We can read this "manualy" using simpler functions. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions. + let mut reader = EvmDataReader::new(&writer_output); - // We pad data to a multiple of 32 bytes. - let mut padded = data.to_vec(); - assert!(data.len() < 0x80); - padded.resize(0x80, 0); + // We pad data to a multiple of 32 bytes. + let mut padded = data.to_vec(); + assert!(data.len() < 0x80); + padded.resize(0x80, 0); - assert_eq!(reader.read::().expect("read offset"), 32.into()); - assert_eq!(reader.read::().expect("read size"), data.len().into()); - let mut read = |e| reader.read::().expect(e); // shorthand - assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); - assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); - assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); - assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), data.len().into()); + let mut read = |e| reader.read::().expect(e); // shorthand + assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); + assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); + assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); + assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); } #[test] fn read_string() { - let data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new().write(Bytes::from(data)).build(); + let writer_output = EvmDataWriter::new().write(Bytes::from(data)).build(); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Bytes = reader.read().expect("to correctly parse Bytes"); - assert_eq!(data, parsed.as_str().expect("valid utf8")); + assert_eq!(data, parsed.as_str().expect("valid utf8")); } #[test] fn write_string() { - let data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new().write(Bytes::from(data)).build(); + let writer_output = EvmDataWriter::new().write(Bytes::from(data)).build(); - // We can read this "manualy" using simpler functions. - let mut reader = EvmDataReader::new(&writer_output); + // We can read this "manualy" using simpler functions. + let mut reader = EvmDataReader::new(&writer_output); - // We pad data to next multiple of 32 bytes. - let mut padded = data.as_bytes().to_vec(); - assert!(data.len() < 0x80); - padded.resize(0x80, 0); + // We pad data to next multiple of 32 bytes. + let mut padded = data.as_bytes().to_vec(); + assert!(data.len() < 0x80); + padded.resize(0x80, 0); - assert_eq!(reader.read::().expect("read offset"), 32.into()); - assert_eq!(reader.read::().expect("read size"), data.len().into()); - let mut read = |e| reader.read::().expect(e); // shorthand - assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); - assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); - assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); - assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); + assert_eq!(reader.read::().expect("read offset"), 32.into()); + assert_eq!(reader.read::().expect("read size"), data.len().into()); + let mut read = |e| reader.read::().expect(e); // shorthand + assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); + assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); + assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); + assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); } #[test] fn write_vec_bytes() { - let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new() - .write(vec![Bytes::from(&data[..]), Bytes::from(&data[..])]) - .build(); + let writer_output = EvmDataWriter::new() + .write(vec![Bytes::from(&data[..]), Bytes::from(&data[..])]) + .build(); - writer_output - .chunks_exact(32) - .map(|chunk| H256::from_slice(chunk)) - .for_each(|hash| println!("{:?}", hash)); + writer_output + .chunks_exact(32) + .map(|chunk| H256::from_slice(chunk)) + .for_each(|hash| println!("{:?}", hash)); - // We pad data to a multiple of 32 bytes. - let mut padded = data.to_vec(); - assert!(data.len() < 0x80); - padded.resize(0x80, 0); + // We pad data to a multiple of 32 bytes. + let mut padded = data.to_vec(); + assert!(data.len() < 0x80); + padded.resize(0x80, 0); - let mut reader = EvmDataReader::new(&writer_output); + let mut reader = EvmDataReader::new(&writer_output); - // Offset of vec - assert_eq!(reader.read::().expect("read offset"), 32.into()); + // Offset of vec + assert_eq!(reader.read::().expect("read offset"), 32.into()); - // Length of vec - assert_eq!(reader.read::().expect("read offset"), 2.into()); + // Length of vec + assert_eq!(reader.read::().expect("read offset"), 2.into()); - // Relative offset of first bytgmes object - assert_eq!(reader.read::().expect("read offset"), 0x40.into()); - // Relative offset of second bytes object - assert_eq!(reader.read::().expect("read offset"), 0xe0.into()); + // Relative offset of first bytgmes object + assert_eq!(reader.read::().expect("read offset"), 0x40.into()); + // Relative offset of second bytes object + assert_eq!(reader.read::().expect("read offset"), 0xe0.into()); - // Length of first bytes object - assert_eq!(reader.read::().expect("read size"), data.len().into()); + // Length of first bytes object + assert_eq!(reader.read::().expect("read size"), data.len().into()); - // First byte objects data - let mut read = |e| reader.read::().expect(e); // shorthand - assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); - assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); - assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); - assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); + // First byte objects data + let mut read = |e| reader.read::().expect(e); // shorthand + assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); + assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); + assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); + assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); - // Length of second bytes object - assert_eq!(reader.read::().expect("read size"), data.len().into()); + // Length of second bytes object + assert_eq!(reader.read::().expect("read size"), data.len().into()); - // Second byte objects data - let mut read = |e| reader.read::().expect(e); // shorthand - assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); - assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); - assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); - assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); + // Second byte objects data + let mut read = |e| reader.read::().expect(e); // shorthand + assert_eq!(read("read part 1"), H256::from_slice(&padded[0x00..0x20])); + assert_eq!(read("read part 2"), H256::from_slice(&padded[0x20..0x40])); + assert_eq!(read("read part 3"), H256::from_slice(&padded[0x40..0x60])); + assert_eq!(read("read part 4"), H256::from_slice(&padded[0x60..0x80])); } #[test] fn read_vec_of_bytes() { - let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ + let data = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod\ tempor incididunt ut labore et dolore magna aliqua."; - let writer_output = EvmDataWriter::new() - .write(vec![Bytes::from(&data[..]), Bytes::from(&data[..])]) - .build(); + let writer_output = EvmDataWriter::new() + .write(vec![Bytes::from(&data[..]), Bytes::from(&data[..])]) + .build(); - writer_output - .chunks_exact(32) - .map(|chunk| H256::from_slice(chunk)) - .for_each(|hash| println!("{:?}", hash)); + writer_output + .chunks_exact(32) + .map(|chunk| H256::from_slice(chunk)) + .for_each(|hash| println!("{:?}", hash)); - let mut reader = EvmDataReader::new(&writer_output); - let parsed: Vec = reader.read().expect("to correctly parse Vec"); + let mut reader = EvmDataReader::new(&writer_output); + let parsed: Vec = reader.read().expect("to correctly parse Vec"); - assert_eq!(vec![Bytes::from(&data[..]), Bytes::from(&data[..])], parsed); + assert_eq!(vec![Bytes::from(&data[..]), Bytes::from(&data[..])], parsed); } // The following test parses input data generated by web3 from a Solidity contract. @@ -655,36 +655,36 @@ fn read_vec_of_bytes() { #[derive(Clone, Debug, Eq, PartialEq)] struct MultiLocation { - parents: u8, - interior: Vec, + parents: u8, + interior: Vec, } impl EvmData for MultiLocation { - fn read(reader: &mut EvmDataReader) -> EvmResult { - let (parents, interior) = reader.read()?; - Ok(MultiLocation { parents, interior }) - } + fn read(reader: &mut EvmDataReader) -> EvmResult { + let (parents, interior) = reader.read()?; + Ok(MultiLocation { parents, interior }) + } - fn write(writer: &mut EvmDataWriter, value: Self) { - EvmData::write(writer, (value.parents, value.interior)); - } + fn write(writer: &mut EvmDataWriter, value: Self) { + EvmData::write(writer, (value.parents, value.interior)); + } - fn has_static_size() -> bool { - <(u8, Vec)>::has_static_size() - } + fn has_static_size() -> bool { + <(u8, Vec)>::has_static_size() + } } #[crate::generate_function_selector] #[derive(Debug, PartialEq)] pub enum Action { - TransferMultiAsset = "transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),uint64)", + TransferMultiAsset = "transfer_multiasset((uint8,bytes[]),uint256,(uint8,bytes[]),uint64)", } #[test] fn read_complex_solidity_function() { - // Function call data generated by web3. - let data = hex!( - "b38c60fa + // Function call data generated by web3. + let data = hex!( + "b38c60fa 0000000000000000000000000000000000000000000000000000000000000080 0000000000000000000000000000000000000000000000000000000000000064 00000000000000000000000000000000000000000000000000000000000001a0 @@ -705,184 +705,179 @@ fn read_complex_solidity_function() { 0000000000000000000000000000000000000000000000000000000000000022 0101010101010101010101010101010101010101010101010101010101010101 0100000000000000000000000000000000000000000000000000000000000000" - ); - - let selector = EvmDataReader::read_selector::(&data).expect("to read selector"); - let mut reader = EvmDataReader::new_skip_selector(&data).expect("to read selector"); - - assert_eq!(selector, Action::TransferMultiAsset); - // asset - assert_eq!( - reader.read::().unwrap(), - MultiLocation { - parents: 1, - interior: vec![ - Bytes::from(&hex!("00000003e8")[..]), - Bytes::from(&hex!("0403")[..]), - ], - } - ); - - // amount - assert_eq!(reader.read::().unwrap(), 100u32.into()); - - // destination - assert_eq!( - reader.read::().unwrap(), - MultiLocation { - parents: 1, - interior: vec![Bytes::from( - &hex!("01010101010101010101010101010101010101010101010101010101010101010100")[..] - )], - } - ); - - // weight - assert_eq!(reader.read::().unwrap(), 100u32.into()); + ); + + let selector = EvmDataReader::read_selector::(&data).expect("to read selector"); + let mut reader = EvmDataReader::new_skip_selector(&data).expect("to read selector"); + + assert_eq!(selector, Action::TransferMultiAsset); + // asset + assert_eq!( + reader.read::().unwrap(), + MultiLocation { + parents: 1, + interior: vec![ + Bytes::from(&hex!("00000003e8")[..]), + Bytes::from(&hex!("0403")[..]), + ], + } + ); + + // amount + assert_eq!(reader.read::().unwrap(), 100u32.into()); + + // destination + assert_eq!( + reader.read::().unwrap(), + MultiLocation { + parents: 1, + interior: vec![Bytes::from( + &hex!("01010101010101010101010101010101010101010101010101010101010101010100")[..] + )], + } + ); + + // weight + assert_eq!(reader.read::().unwrap(), 100u32.into()); } - - - - - #[test] fn test_check_function_modifier() { - let context = |value: u32| Context { - address: H160::zero(), - caller: H160::zero(), - apparent_value: U256::from(value), - }; - - let payable_error = || revert("function is not payable"); - let static_error = || revert("can't call non-static function in static context"); - - // Can't call non-static functions in static context. - assert_eq!( - check_function_modifier(&context(0), true, FunctionModifier::Payable), - Err(static_error()) - ); - assert_eq!( - check_function_modifier(&context(0), true, FunctionModifier::NonPayable), - Err(static_error()) - ); - assert_eq!( - check_function_modifier(&context(0), true, FunctionModifier::View), - Ok(()) - ); - - // Static check is performed before non-payable check. - assert_eq!( - check_function_modifier(&context(1), true, FunctionModifier::Payable), - Err(static_error()) - ); - assert_eq!( - check_function_modifier(&context(1), true, FunctionModifier::NonPayable), - Err(static_error()) - ); - // FunctionModifier::View pass static check but fail for payable. - assert_eq!( - check_function_modifier(&context(1), true, FunctionModifier::View), - Err(payable_error()) - ); - - // Can't send funds to non payable function - assert_eq!( - check_function_modifier(&context(1), false, FunctionModifier::Payable), - Ok(()) - ); - assert_eq!( - check_function_modifier(&context(1), false, FunctionModifier::NonPayable), - Err(payable_error()) - ); - assert_eq!( - check_function_modifier(&context(1), false, FunctionModifier::View), - Err(payable_error()) - ); - - // Any function can be called without funds. - assert_eq!( - check_function_modifier(&context(0), false, FunctionModifier::Payable), - Ok(()) - ); - assert_eq!( - check_function_modifier(&context(0), false, FunctionModifier::NonPayable), - Ok(()) - ); - assert_eq!( - check_function_modifier(&context(0), false, FunctionModifier::View), - Ok(()) - ); + let context = |value: u32| Context { + address: H160::zero(), + caller: H160::zero(), + apparent_value: U256::from(value), + }; + + let payable_error = || revert("function is not payable"); + let static_error = || revert("can't call non-static function in static context"); + + // Can't call non-static functions in static context. + assert_eq!( + check_function_modifier(&context(0), true, FunctionModifier::Payable), + Err(static_error()) + ); + assert_eq!( + check_function_modifier(&context(0), true, FunctionModifier::NonPayable), + Err(static_error()) + ); + assert_eq!( + check_function_modifier(&context(0), true, FunctionModifier::View), + Ok(()) + ); + + // Static check is performed before non-payable check. + assert_eq!( + check_function_modifier(&context(1), true, FunctionModifier::Payable), + Err(static_error()) + ); + assert_eq!( + check_function_modifier(&context(1), true, FunctionModifier::NonPayable), + Err(static_error()) + ); + // FunctionModifier::View pass static check but fail for payable. + assert_eq!( + check_function_modifier(&context(1), true, FunctionModifier::View), + Err(payable_error()) + ); + + // Can't send funds to non payable function + assert_eq!( + check_function_modifier(&context(1), false, FunctionModifier::Payable), + Ok(()) + ); + assert_eq!( + check_function_modifier(&context(1), false, FunctionModifier::NonPayable), + Err(payable_error()) + ); + assert_eq!( + check_function_modifier(&context(1), false, FunctionModifier::View), + Err(payable_error()) + ); + + // Any function can be called without funds. + assert_eq!( + check_function_modifier(&context(0), false, FunctionModifier::Payable), + Ok(()) + ); + assert_eq!( + check_function_modifier(&context(0), false, FunctionModifier::NonPayable), + Ok(()) + ); + assert_eq!( + check_function_modifier(&context(0), false, FunctionModifier::View), + Ok(()) + ); } #[test] fn read_static_size_tuple() { - // (address, uint256) encoded by web3 - let data = hex!( - "0000000000000000000000001111111111111111111111111111111111111111 + // (address, uint256) encoded by web3 + let data = hex!( + "0000000000000000000000001111111111111111111111111111111111111111 0000000000000000000000000000000000000000000000000000000000000001" - ); + ); - let mut reader = EvmDataReader::new(&data); + let mut reader = EvmDataReader::new(&data); - assert_eq!( - reader.read::<(Address, U256)>().unwrap(), - (Address(H160::repeat_byte(0x11)), U256::from(1u8)) - ); + assert_eq!( + reader.read::<(Address, U256)>().unwrap(), + (Address(H160::repeat_byte(0x11)), U256::from(1u8)) + ); } #[test] fn read_dynamic_size_tuple() { - // (uint8, bytes[]) encoded by web3 - let data = hex!( - "0000000000000000000000000000000000000000000000000000000000000020 + // (uint8, bytes[]) encoded by web3 + let data = hex!( + "0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000040 0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000001 0100000000000000000000000000000000000000000000000000000000000000" - ); + ); - let mut reader = EvmDataReader::new(&data); + let mut reader = EvmDataReader::new(&data); - assert_eq!( - reader.read::<(u8, Vec)>().unwrap(), - (1, vec![Bytes(vec![0x01])]) - ); + assert_eq!( + reader.read::<(u8, Vec)>().unwrap(), + (1, vec![Bytes(vec![0x01])]) + ); } #[test] fn write_static_size_tuple() { - let output = EvmDataWriter::new() - .write((Address(H160::repeat_byte(0x11)), U256::from(1u8))) - .build(); + let output = EvmDataWriter::new() + .write((Address(H160::repeat_byte(0x11)), U256::from(1u8))) + .build(); - // (address, uint256) encoded by web3 - let data = hex!( - "0000000000000000000000001111111111111111111111111111111111111111 + // (address, uint256) encoded by web3 + let data = hex!( + "0000000000000000000000001111111111111111111111111111111111111111 0000000000000000000000000000000000000000000000000000000000000001" - ); + ); - assert_eq!(output, data); + assert_eq!(output, data); } #[test] fn write_dynamic_size_tuple() { - let output = EvmDataWriter::new() - .write((1u8, vec![Bytes(vec![0x01])])) - .build(); + let output = EvmDataWriter::new() + .write((1u8, vec![Bytes(vec![0x01])])) + .build(); - // (uint8, bytes[]) encoded by web3 - let data = hex!( - "0000000000000000000000000000000000000000000000000000000000000020 + // (uint8, bytes[]) encoded by web3 + let data = hex!( + "0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000040 0000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000001 0100000000000000000000000000000000000000000000000000000000000000" - ); + ); - assert_eq!(output, data); -} \ No newline at end of file + assert_eq!(output, data); +} diff --git a/runtime/frost/Cargo.toml b/runtime/frost/Cargo.toml index 443bf8b6..76bb53dd 100644 --- a/runtime/frost/Cargo.toml +++ b/runtime/frost/Cargo.toml @@ -97,6 +97,14 @@ pallet-fees-split = { path = "../../pallets/fees-split", default-features = fals pallet-airdrop = { path = "../../pallets/airdrop", default-features = false } assets-evm ={ path="../common/assets-evm", default-features = false} +# RMRK dependencies +pallet-uniques = { git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.36", default-features = false } +pallet-rmrk-core = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36", default-features = false } +pallet-rmrk-equip = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36", default-features = false } +pallet-rmrk-market = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36", default-features = false } +rmrk-traits = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36", default-features = false } +pallet-rmrk-rpc-runtime-api = { git = "https://github.com/web3labs/rmrk-substrate", branch = "polkadot-v0.9.36", default-features = false } + # ice-runtime-common = { default-features = false, path = "../common", optional = true } # try-runtime @@ -129,6 +137,13 @@ std = [ "fp-rpc/std", "fp-self-contained/std", + "pallet-rmrk-core/std", + "pallet-rmrk-market/std", + "pallet-rmrk-equip/std", + "rmrk-traits/std", + "pallet-rmrk-rpc-runtime-api/std", + "pallet-uniques/std", + "pallet-airdrop/std", "pallet-assets/std", "pallet-aura/std", diff --git a/runtime/frost/src/lib.rs b/runtime/frost/src/lib.rs index 8a14c5d4..0ac13e1a 100644 --- a/runtime/frost/src/lib.rs +++ b/runtime/frost/src/lib.rs @@ -18,6 +18,7 @@ use pallet_grandpa::{ use frame_support::{ pallet_prelude::ConstU32, + traits::tokens::nonfungibles::*, traits::{ AsEnsureOriginWithArg, ConstBool, EitherOfDiverse, EqualPrivilegeOnly, InstanceFilter, LockIdentifier, WithdrawReasons, @@ -43,6 +44,10 @@ use sp_runtime::{ transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, ApplyExtrinsicResult, FixedPointNumber, MultiSignature, Perquintill, }; +use sp_std::{marker::PhantomData, prelude::*}; +#[cfg(feature = "std")] +use sp_version::NativeVersion; +use sp_version::RuntimeVersion; use weights::{ AirdropWeightInfo, AssetsWeightInfo, BalancesWeightInfo, CollectiveWeightInfo, ContractsWeightInfo, DemocracyWeightInfo, GrandpaWeightInfo, IdentityWeightInfo, @@ -51,14 +56,21 @@ use weights::{ UtilityWeightInfo, VestingWeightInfo, }; -use sp_std::{marker::PhantomData, prelude::*}; -#[cfg(feature = "std")] -use sp_version::NativeVersion; -use sp_version::RuntimeVersion; - use frame_support::inherent::Vec; +use frame_support::traits::Contains; use sp_std::boxed::Box; +use frame_support::BoundedVec; +use pallet_rmrk_core::{CollectionInfoOf, InstanceInfoOf, PropertyInfoOf, ResourceInfoOf}; +use pallet_rmrk_equip::{BaseInfoOf, BoundedThemeOf, PartTypeOf}; +use rmrk_traits::{ + primitives::*, + primitives::{CollectionId, NftId, ResourceId}, + NftChild, +}; +use sp_runtime::DispatchError; +use sp_std::collections::btree_set::BTreeSet; + pub mod constants; pub mod impls; pub use constants::{currency, time::*}; @@ -203,11 +215,37 @@ parameter_types! { pub const SS58Prefix: u16 = 2208; } +pub struct BaseFilter; +impl Contains for BaseFilter { + fn contains(call: &RuntimeCall) -> bool { + // Disable direct calls to pallet_uniques + !matches!( + call, + RuntimeCall::Uniques(pallet_uniques::Call::approve_transfer { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::burn { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::cancel_approval { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::clear_collection_metadata { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::clear_metadata { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::create { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::destroy { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::force_item_status { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::force_create { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::freeze_collection { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::mint { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::redeposit { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::set_collection_metadata { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::thaw_collection { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::transfer { .. }) + | RuntimeCall::Uniques(pallet_uniques::Call::transfer_ownership { .. }) + ) + } +} + // Configure FRAME pallets to include in runtime. impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = BaseFilter; // frame_support::traits::Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = RuntimeBlockWeights; /// The maximum length of a block (in bytes). @@ -1084,6 +1122,93 @@ impl pallet_base_fee::Config for Runtime { impl pallet_randomness_collective_flip::Config for Runtime {} +// Remark Pallets + +parameter_types! { + pub const ResourceSymbolLimit: u32 = 10; + pub const PartsLimit: u32 = 25; + pub const MaxPriorities: u32 = 25; + pub const CollectionSymbolLimit: u32 = 100; + pub const MaxResourcesOnMint: u32 = 100; + pub const PropertiesLimit: u32 = 25; + pub const NestingBudget: u32 = 20; +} + +#[cfg(feature = "runtime-benchmarks")] +use pallet_rmrk_core::RmrkBenchmark; + +impl pallet_rmrk_core::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProtocolOrigin = frame_system::EnsureRoot; + type ResourceSymbolLimit = ResourceSymbolLimit; + type PartsLimit = PartsLimit; + type MaxPriorities = MaxPriorities; + type CollectionSymbolLimit = CollectionSymbolLimit; + type MaxResourcesOnMint = MaxResourcesOnMint; + type PropertiesLimit = PropertiesLimit; + type NestingBudget = NestingBudget; + type WeightInfo = pallet_rmrk_core::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type Helper = RmrkBenchmark; + type TransferHooks = (); +} +use crate::currency::CENTS; +use crate::currency::DOLLARS; +parameter_types! { + pub const CollectionDeposit: Balance = 10 * CENTS; + pub const ItemDeposit: Balance = DOLLARS; + pub const KeyLimit: u32 = 32; + pub const ValueLimit: u32 = 256; + pub const UniquesMetadataDepositBase: Balance = 10 * CENTS; + pub const AttributeDepositBase: Balance = 10 * CENTS; + pub const RmrkDepositPerByte: Balance = CENTS; + pub const UniquesStringLimit: u32 = 128; + pub const MaxPropertiesPerTheme: u32 = 100; + pub const MaxCollectionsEquippablePerPart: u32 = 100; +} + +impl pallet_rmrk_equip::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type MaxPropertiesPerTheme = MaxPropertiesPerTheme; + type MaxCollectionsEquippablePerPart = MaxCollectionsEquippablePerPart; + type WeightInfo = pallet_rmrk_equip::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type Helper = RmrkBenchmark; +} + +parameter_types! { + pub const MinimumOfferAmount: Balance = DOLLARS / 10_000; +} + +impl pallet_rmrk_market::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type ProtocolOrigin = frame_system::EnsureRoot; + type Currency = Balances; + type MinimumOfferAmount = MinimumOfferAmount; + type WeightInfo = pallet_rmrk_market::weights::SubstrateWeight; + #[cfg(feature = "runtime-benchmarks")] + type Helper = RmrkBenchmark; +} + +impl pallet_uniques::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type CollectionId = u32; + type ItemId = u32; + type Currency = Balances; + type ForceOrigin = frame_system::EnsureRoot; + type CreateOrigin = AsEnsureOriginWithArg>; + type Locker = pallet_rmrk_core::Pallet; + type CollectionDeposit = CollectionDeposit; + type ItemDeposit = ItemDeposit; + type MetadataDepositBase = UniquesMetadataDepositBase; + type AttributeDepositBase = AttributeDepositBase; + type DepositPerByte = DepositPerByte; + type StringLimit = UniquesStringLimit; + type KeyLimit = KeyLimit; + type ValueLimit = ValueLimit; + type WeightInfo = (); +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime where @@ -1140,6 +1265,12 @@ construct_runtime!( PhragmenElection: pallet_elections_phragmen::{Pallet, Call, Storage, Event, Config} = 63, CouncilMembership: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 64, TechnicalMembership: pallet_membership::::{Pallet, Call, Storage, Event, Config} = 65, + + // Remark Stuff + RmrkEquip: pallet_rmrk_equip::{Pallet, Call, Event, Storage}=66, + RmrkCore: pallet_rmrk_core::{Pallet, Call, Event, Storage}=67, + RmrkMarket: pallet_rmrk_market::{Pallet, Call, Storage, Event}=68, + Uniques: pallet_uniques::{Pallet, Call, Storage, Event}=69, } ); @@ -1685,4 +1816,123 @@ impl_runtime_apis! { Contracts::get_storage(address, key) } } + // rmrk rpc + impl pallet_rmrk_rpc_runtime_api::RmrkApi< + Block, + AccountId, + CollectionInfoOf, + InstanceInfoOf, + ResourceInfoOf, + PropertyInfoOf, + BaseInfoOf, + PartTypeOf, + BoundedThemeOf + > for Runtime + { + fn collection_by_id(id: CollectionId) -> pallet_rmrk_rpc_runtime_api::Result>> { + Ok(RmrkCore::collections(id)) + } + + fn nft_by_id(collection_id: CollectionId, nft_id: NftId) -> pallet_rmrk_rpc_runtime_api::Result>> { + Ok(RmrkCore::nfts(collection_id, nft_id)) + } + + fn account_tokens(account_id: AccountId, collection_id: CollectionId) -> pallet_rmrk_rpc_runtime_api::Result> { + Ok(Uniques::owned_in_collection(&collection_id, &account_id).collect()) + } + + fn nft_children(collection_id: CollectionId, nft_id: NftId) -> pallet_rmrk_rpc_runtime_api::Result>> { + let children = RmrkCore::iterate_nft_children(collection_id, nft_id).collect(); + + Ok(children) + } + + fn collection_properties( + collection_id: CollectionId, + filter_keys: Option> + ) -> pallet_rmrk_rpc_runtime_api::Result>> { + let nft_id = None; + + let filter_keys = option_filter_keys_to_set::<::KeyLimit>( + filter_keys + )?; + + Ok(RmrkCore::query_properties(collection_id, nft_id, filter_keys).collect()) + } + + fn nft_properties( + collection_id: CollectionId, + nft_id: NftId, + filter_keys: Option> + ) -> pallet_rmrk_rpc_runtime_api::Result>> { + let filter_keys = option_filter_keys_to_set::<::KeyLimit>( + filter_keys + )?; + + Ok(RmrkCore::query_properties(collection_id, Some(nft_id), filter_keys).collect()) + } + + fn nft_resources(collection_id: CollectionId, nft_id: NftId) -> pallet_rmrk_rpc_runtime_api::Result>> { + Ok(RmrkCore::iterate_resources(collection_id, nft_id).collect()) + } + + fn nft_resource_priority(collection_id: CollectionId, nft_id: NftId, resource_id: ResourceId) -> pallet_rmrk_rpc_runtime_api::Result> { + let priority = RmrkCore::priorities((collection_id, nft_id, resource_id)); + + Ok(priority) + } + + fn base(base_id: BaseId) -> pallet_rmrk_rpc_runtime_api::Result>> { + Ok(RmrkEquip::bases(base_id)) + } + + fn base_parts(base_id: BaseId) -> pallet_rmrk_rpc_runtime_api::Result>> { + Ok(RmrkEquip::iterate_part_types(base_id).collect()) + } + + fn theme_names(base_id: BaseId) -> pallet_rmrk_rpc_runtime_api::Result> { + let names = RmrkEquip::iterate_theme_names(base_id) + .map(|name| name.into()) + .collect(); + + Ok(names) + } + + fn theme( + base_id: BaseId, + theme_name: pallet_rmrk_rpc_runtime_api::ThemeName, + filter_keys: Option> + ) -> pallet_rmrk_rpc_runtime_api::Result>> { + use pallet_rmrk_equip::StringLimitOf; + + let theme_name: StringLimitOf = theme_name.try_into() + .map_err(|_| DispatchError::Other("Can't read theme_name"))?; + + let filter_keys = option_filter_keys_to_set::<::StringLimit>( + filter_keys + )?; + + let theme = RmrkEquip::get_theme(base_id, theme_name, filter_keys)?; + Ok(theme) + } + } +} +// helper functions +fn option_filter_keys_to_set>( + filter_keys: Option>, +) -> pallet_rmrk_rpc_runtime_api::Result>>> { + match filter_keys { + Some(filter_keys) => { + let tree = filter_keys + .into_iter() + .map(|filter_keys| -> pallet_rmrk_rpc_runtime_api::Result> { + filter_keys + .try_into() + .map_err(|_| DispatchError::Other("Can't read filter key")) + }) + .collect::>>()?; + Ok(Some(tree)) + } + None => Ok(None), + } }