From 415e3d612ac1a044b508986a6633cef157c79129 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Fri, 30 Apr 2021 15:15:31 +0000 Subject: [PATCH] feat: eth address mapping --- --- /dev/null +++ b/pallets/nft/src/eth/account.rs @@ -0,0 +1,135 @@ +use crate::Config; +use codec::{Encode, EncodeLike, Decode}; +use sp_core::{H160, H256, crypto::AccountId32}; +use core::cmp::Ordering; +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; +use pallet_evm::AddressMapping; +use sp_std::vec::Vec; +use sp_std::clone::Clone; + +pub trait CrossAccountId: + Encode + EncodeLike + Decode + + Clone + PartialEq + Ord + core::fmt::Debug // + + // Serialize + Deserialize<'static> +{ + fn as_sub(&self) -> &AccountId; + fn as_eth(&self) -> &H160; + + fn from_sub(account: AccountId) -> Self; + fn from_eth(account: H160) -> Self; +} + +#[derive(Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct BasicCrossAccountId { + /// If true - then ethereum is canonical encoding + from_ethereum: bool, + substrate: T::AccountId, + ethereum: H160, +} + +impl core::fmt::Debug for BasicCrossAccountId { + fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { + if self.from_ethereum { + fmt.debug_tuple("CrossAccountId::Ethereum") + .field(&self.ethereum) + .finish() + } else { + fmt.debug_tuple("CrossAccountId::Substrate") + .field(&self.substrate) + .finish() + } + } +} + +impl PartialOrd for BasicCrossAccountId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.substrate.cmp(&other.substrate)) + } +} + +impl Ord for BasicCrossAccountId { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).expect("substrate account is total ordered") + } +} + +impl PartialEq for BasicCrossAccountId { + fn eq(&self, other: &Self) -> bool { + if self.from_ethereum == other.from_ethereum { + self.substrate == other.substrate && self.ethereum == other.ethereum + } else if self.from_ethereum { + // ethereum is canonical encoding, but we need to compare derived address + self.substrate == other.substrate + } else { + self.ethereum == other.ethereum + } + } +} +impl Clone for BasicCrossAccountId { + fn clone(&self) -> Self { + Self { + from_ethereum: self.from_ethereum, + substrate: self.substrate.clone(), + ethereum: self.ethereum, + } + } +} +impl Encode for BasicCrossAccountId { + fn encode(&self) -> Vec { + let as_result = if !self.from_ethereum { + Ok(self.substrate.clone()) + } else { + Err(self.ethereum) + }; + as_result.encode() + } +} +impl EncodeLike for BasicCrossAccountId {} +impl Decode for BasicCrossAccountId { + fn decode(input: &mut I) -> Result + where I: codec::Input + { + Ok(match >::decode(input)? { + Ok(s) => Self::from_sub(s), + Err(e) => Self::from_eth(e), + }) + } +} +impl CrossAccountId for BasicCrossAccountId { + fn as_sub(&self) -> &T::AccountId { + &self.substrate + } + fn as_eth(&self) -> &H160 { + &self.ethereum + } + fn from_sub(substrate: T::AccountId) -> Self { + Self { + ethereum: T::EvmBackwardsAddressMapping::from_account_id(substrate.clone()), + substrate, + from_ethereum: false, + } + } + fn from_eth(ethereum: H160) -> Self { + Self { + ethereum, + substrate: T::EvmAddressMapping::into_account_id(ethereum), + from_ethereum: true, + } + } +} + +pub trait EvmBackwardsAddressMapping { + fn from_account_id(account_id: AccountId) -> H160; +} + +/// Should have same mapping as EnsureAddressTruncated +pub struct MapBackwardsAddressTruncated; +impl EvmBackwardsAddressMapping for MapBackwardsAddressTruncated { + fn from_account_id(account_id: AccountId32) -> H160 { + let mut out = [0; 20]; + out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]); + H160(out) + } +} \ No newline at end of file --- /dev/null +++ b/pallets/nft/src/eth/mod.rs @@ -0,0 +1,2 @@ +pub mod account; +use account::CrossAccountId; --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -8,9 +8,6 @@ #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] -pub use std::*; - -#[cfg(feature = "std")] pub use serde::*; use core::ops::{Deref, DerefMut}; @@ -33,6 +30,7 @@ }; use frame_system::{self as system, ensure_signed, ensure_root}; +use sp_core::{H160, H256}; use sp_runtime::sp_std::prelude::Vec; use sp_runtime::{ traits::{ @@ -45,6 +43,7 @@ }; use sp_runtime::traits::StaticLookup; use pallet_contracts::chain_extension::UncheckedFrom; +use pallet_evm::AddressMapping; use pallet_transaction_payment::OnChargeTransaction; #[cfg(test)] @@ -54,6 +53,9 @@ mod tests; mod default_weights; +mod eth; + +pub use eth::account::*; pub const MAX_DECIMAL_POINTS: DecimalPoints = 30; pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000; @@ -164,7 +166,7 @@ #[derive(Encode, Decode, Clone, PartialEq)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct Collection { - pub owner: T::AccountId, + pub owner: T::CrossAccountId, pub mode: CollectionMode, pub access: AccessMode, pub decimal_points: DecimalPoints, @@ -174,7 +176,7 @@ pub mint_mode: bool, pub offchain_schema: Vec, pub schema_version: SchemaVersion, - pub sponsorship: SponsorshipState, + pub sponsorship: SponsorshipState, pub limits: CollectionLimits, // Collection private restrictions pub variable_on_chain_schema: Vec, // pub const_on_chain_schema: Vec, // @@ -461,6 +463,11 @@ /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + type EvmAddressMapping: pallet_evm::AddressMapping; + type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping; + type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin; + + type CrossAccountId: CrossAccountId; type Currency: Currency; type CollectionCreationPrice: Get<<::Currency as Currency>::Balance>; type TreasuryAccountId: Get; @@ -525,7 +532,7 @@ pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option> = None; /// List of collection admins /// Collection id (controlled?2) - pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec; + pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec; /// Whitelisted collection users /// Collection id (controlled?2), user id (controlled?3) pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool; @@ -542,11 +549,11 @@ //#region Item collections /// Collection id (controlled?2), token id (controlled?1) - pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option>; + pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option>; /// Collection id (controlled?2), owner (controlled?2) pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType; /// Collection id (controlled?2), token id (controlled?1) - pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option>; + pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option>; //#endregion //#region Index list @@ -610,7 +617,7 @@ decl_event!( pub enum Event where - AccountId = ::AccountId, + CrossAccountId = ::CrossAccountId, { /// New collection was created /// @@ -621,7 +628,7 @@ /// * mode: [CollectionMode] converted into u8. /// /// * account_id: Collection owner. - CollectionCreated(CollectionId, u8, AccountId), + CollectionCreated(CollectionId, u8, CrossAccountId), /// New item was created. /// @@ -632,7 +639,7 @@ /// * item_id: Id of an item. Unique within the collection. /// /// * recipient: Owner of newly created item - ItemCreated(CollectionId, TokenId, AccountId), + ItemCreated(CollectionId, TokenId, CrossAccountId), /// Collection item was burned. /// @@ -654,7 +661,7 @@ /// * recipient: New owner of item /// /// * amount: Always 1 for NFT - Transfer(CollectionId, TokenId, AccountId, AccountId, u128), + Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128), /// * collection_id /// @@ -665,7 +672,7 @@ /// * spender /// /// * amount - Approved(CollectionId, TokenId, AccountId, AccountId, u128), + Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128), } ); --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -578,6 +578,12 @@ impl pallet_nft::Config for Runtime { type Event = Event; type WeightInfo = nft_weights::WeightInfo; + + type EvmWithdrawOrigin = EnsureAddressTruncated; + type EvmBackwardsAddressMapping = pallet_nft::MapBackwardsAddressTruncated; + type EvmAddressMapping = HashedAddressMapping; + type CrossAccountId = pallet_nft::BasicCrossAccountId; + type Currency = Balances; type CollectionCreationPrice = CollectionCreationPrice; type TreasuryAccountId = TreasuryAccountId; --- a/runtime_types.json +++ b/runtime_types.json @@ -1,4 +1,10 @@ { + "CrossAccountId": { + "_enum": { + "substrate": "AccountId", + "ethereum": "H160" + } + }, "AccessMode": { "_enum": [ "Normal", @@ -15,31 +21,31 @@ } }, "Ownership": { - "Owner": "AccountId", + "Owner": "CrossAccountId", "Fraction": "u128" }, "FungibleItemType": { "Value": "u128" }, "NftItemType": { - "Owner": "AccountId", + "Owner": "CrossAccountId", "ConstData": "Vec", "VariableData": "Vec" }, "ReFungibleItemType": { - "Owner": "Vec>", + "Owner": "Vec>", "ConstData": "Vec", "VariableData": "Vec" }, "SponsorshipState": { "_enum": { - "Disabled": null, - "Unconfirmed": "AccountId", - "Confirmed": "AccountId" + "disabled": null, + "unconfirmed": "CrossAccountId", + "confirmed": "CrossAccountId" } }, "Collection": { - "Owner": "AccountId", + "Owner": "CrossAccountId", "Mode": "CollectionMode", "Access": "AccessMode", "DecimalPoints": "DecimalPoints", -- gitstuff