From 977cc46080e3d698171aa0bd04d1231b07d91f86 Mon Sep 17 00:00:00 2001 From: yaro <0623forbidden@gmail.com> Date: Wed, 6 Jul 2022 19:39:43 +0300 Subject: [PATCH 1/2] (asset-lock): Checkpoint --- Cargo.lock | 11 ++ deip_common/deip_asset_system/Cargo.toml | 2 + deip_common/deip_asset_system/src/nft_impl.rs | 112 +++++++++++++++--- 3 files changed, 111 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6ff523fe..2899d325 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1407,6 +1407,7 @@ name = "deip-asset-system" version = "0.1.0" dependencies = [ "deip-assets-error", + "deip-transaction-ctx", "deip_serializable_u128", "frame-support", "frame-system", @@ -1642,6 +1643,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "draft" +version = "0.1.0" +dependencies = [ + "frame-support", + "pallet-timestamp", + "parity-scale-codec", + "rustc-hex", +] + [[package]] name = "dtoa" version = "0.4.8" diff --git a/deip_common/deip_asset_system/Cargo.toml b/deip_common/deip_asset_system/Cargo.toml index a01ea7a9..bcf922aa 100644 --- a/deip_common/deip_asset_system/Cargo.toml +++ b/deip_common/deip_asset_system/Cargo.toml @@ -15,6 +15,7 @@ sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polka # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html deip-assets-error = { path = "../deip_assets_error", default-features = false } deip_serializable_u128 = { path = "../../deip_common/deip_serializable_u128", default-features = false } +deip-transaction-ctx = { path = "../../deip_common/deip_transaction_ctx", default-features = false } # alias "parity-scale-code" to "codec" [dependencies.codec] @@ -37,4 +38,5 @@ std = [ "frame-support/std", "deip-assets-error/std", "deip_serializable_u128/std", + "deip-transaction-ctx/std", ] diff --git a/deip_common/deip_asset_system/src/nft_impl.rs b/deip_common/deip_asset_system/src/nft_impl.rs index f592875c..bd522e08 100644 --- a/deip_common/deip_asset_system/src/nft_impl.rs +++ b/deip_common/deip_asset_system/src/nft_impl.rs @@ -4,12 +4,15 @@ use frame_support::pallet_prelude::*; use sp_runtime::traits::{Hash, AtLeast32BitUnsigned, One, Zero, CheckedAdd, Saturating, CheckedSub}; use codec::{Encode, Decode}; use scale_info::TypeInfo; +use deip_transaction_ctx::{TransactionCtxT, TransactionCtxId}; use crate::{error::Error, FTImplT, Seal}; pub trait NFTImplT { + type TransactionCtx: TransactionCtxT; + type Fungibles: FTImplT< Account=Self::Account, FTokenId=Self::FTokenId, @@ -59,6 +62,8 @@ pub trait NFTImplT Self::Fractional >; + type LockGuard: LockGuardT + Parameter; // LockGuard<...> + type FractionHolderId: From + Copy + Parameter + 'static; type FractionHoldGuard: AtLeast32BitUnsigned + Copy + Parameter + 'static; type FractionHolds: StorageNMap< @@ -71,6 +76,17 @@ pub trait NFTImplT (Self::FractionHolderId, Self::FractionHoldGuard) >; + type FractionTransferLockNumber: StorageDoubleMap< + ::BlockNumber, + ::ExtrinsicId, + u32 + >; + type ItemTransferLockNumber: StorageDoubleMap< + ::BlockNumber, + ::ExtrinsicId, + u32 + >; + type NextCollectionId: StorageValue; type Nonfungibles: @@ -108,7 +124,7 @@ pub trait NFTImplT new_amount, Self::FractionHoldGuard::zero(), ); - + Self::_insert_fractional(&fingerprint, &new_item_fractional_part, seal); Self::_insert_fraction(fraction, seal); @@ -338,7 +354,9 @@ pub trait NFTImplT Self::Fungibles::mint_ft(ft_id, who, amount, seal)?; - let total_amount = *fractional.total() + amount; + let total_amount = fractional.total() + .checked_add(amount) + .ok_or_else(|| Self::Error::overflow().into()); Self::_update_fractions_amount(item, ft_id, total_amount, seal); @@ -464,11 +482,22 @@ pub trait NFTImplT fn hold_fraction( mut fraction: Self::FractionRecord, - holder_id: Self::FractionHolderId, - guard: Self::FractionHoldGuard, _: Seal - ) -> DispatchResult + ) -> Result { + let timepoint = Self::TransactionCtx::current().id(); + + let guard_number + = Self::LockGuard::obtain_number::< + Self::FractionTransferLockNumber + >(&timepoint, Seal(())) + .unwrap_or_else(|| Self::Error::overflow().into())?; + + let guard = Self::LockGuard::new( + timepoint, + guard_number + ); + let key = Self::_fraction_hold_key(&fraction, holder_id, guard, Seal(())); ensure!(!Self::FractionHolds::contains_key(&key), Self::Error::no_permission()); @@ -484,8 +513,7 @@ pub trait NFTImplT fn unhold_fraction( mut fraction: Self::FractionRecord, - holder_id: Self::FractionHolderId, - guard: Self::FractionHoldGuard, + guard: Self::LockGuard, _: Seal ) -> DispatchResult { @@ -503,7 +531,7 @@ pub trait NFTImplT } } -// +// Collection record trait: pub trait CollectionRecordT: Sized { @@ -544,7 +572,7 @@ pub trait CollectionRecordT: Sized } } -// +// Item record trait: pub trait ItemRecordT: Sized { fn account(&self) -> &Impl::Account; @@ -586,7 +614,7 @@ pub trait ItemRecordT: Sized { } } -// +// Fractional: pub trait FractionalT: Sized { fn ft_id(&self) -> &Impl::FTokenId; @@ -618,7 +646,7 @@ impl FractionalT for (Impl::FTokenId, Impl::Fract } } -// +// Fraction record trait: pub trait FractionRecordT: Sized { fn account(&self) -> &Impl::Account; @@ -674,7 +702,7 @@ pub trait FractionRecordT: Sized { } } -// +// Collection record: #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Debug)] pub struct NFTokenCollectionRecord { @@ -739,7 +767,7 @@ impl CollectionRecordT for } } -// +// Item record: #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Debug)] pub struct NFTokenItemRecord { @@ -805,7 +833,7 @@ impl ItemRecordT for } } -// +// Fraction record: #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Debug)] pub struct NFTokenFractionRecord { @@ -870,3 +898,59 @@ impl FractionRecordT &mut self.holds } } + +// Locks: + +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Debug)] +pub struct LockGuard { + timepoint: (BlockNumber, ExtrinsicIndex), + number: LockNumber +} + +pub trait LockGuardT: Sized +{ + fn new( + timepoint: TransactionCtxId, + number: u32 + ) -> Self; + + fn obtain_number> + ( + timepoint: &TransactionCtxId, + _: Seal + ) -> Option + { + let number = S::try_get( + &timepoint.block_number, + &timepoint.extrinsic_id + ).unwrap_or_else(|| 0u32); + S::put(timepoint, number.checked_add(1u32)?); + Some(number) + } +} + +impl LockGuardT + for LockGuard< + Ctx::BlockNumber, + Ctx::ExtrinsicId, + u32 + > +{ + fn new( + timepoint: TransactionCtxId, + number: u32 + ) -> Self + { + let TransactionCtxId { + block_number, extrinsic_id + } = timepoint; + Self { + timepoint: (block_number, extrinsic_id), + number + } + } +} From 29db43e1250b763d3ca6cfd15b91bdef61998733 Mon Sep 17 00:00:00 2001 From: yaro <0623forbidden@gmail.com> Date: Thu, 7 Jul 2022 19:18:59 +0300 Subject: [PATCH 2/2] (asset-lock): Checkpoint --- Cargo.lock | 11 ++ deip_common/deip_asset_system/Cargo.toml | 1 + deip_common/deip_asset_system/src/nft_impl.rs | 158 +++++++++++++++++- 3 files changed, 161 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2899d325..1962df18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -636,6 +636,16 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitmask-enum" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76487de46597d345d040a1be49a6fb636b71d0abab4696b7f3492e0cd4639c73" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "bitvec" version = "0.20.4" @@ -1406,6 +1416,7 @@ dependencies = [ name = "deip-asset-system" version = "0.1.0" dependencies = [ + "bitmask-enum", "deip-assets-error", "deip-transaction-ctx", "deip_serializable_u128", diff --git a/deip_common/deip_asset_system/Cargo.toml b/deip_common/deip_asset_system/Cargo.toml index bcf922aa..c3909471 100644 --- a/deip_common/deip_asset_system/Cargo.toml +++ b/deip_common/deip_asset_system/Cargo.toml @@ -16,6 +16,7 @@ sp-core = { git = "https://github.com/paritytech/substrate.git", branch = "polka deip-assets-error = { path = "../deip_assets_error", default-features = false } deip_serializable_u128 = { path = "../../deip_common/deip_serializable_u128", default-features = false } deip-transaction-ctx = { path = "../../deip_common/deip_transaction_ctx", default-features = false } +bitmask-enum = "2.0.0" # alias "parity-scale-code" to "codec" [dependencies.codec] diff --git a/deip_common/deip_asset_system/src/nft_impl.rs b/deip_common/deip_asset_system/src/nft_impl.rs index bd522e08..93430cc7 100644 --- a/deip_common/deip_asset_system/src/nft_impl.rs +++ b/deip_common/deip_asset_system/src/nft_impl.rs @@ -480,6 +480,49 @@ pub trait NFTImplT Ok(()) } + fn lock_collection( + mut collection: Self::CollectionRecord, + _: Seal + ) -> Result + { + todo!() + } + + fn lock_item( + mut item: Self::ItemRecord, + mask: ItemLockMask, + _: Seal + ) -> Result + { + todo!() + } + + fn lock_fraction( + mut fraction: Self::FractionRecord, + mask: FractionLockMask, + seal: Seal + ) -> Result + { + fraction.lock(mask); + Self::_insert_fraction(fraction, seal); + + let timepoint = Self::TransactionCtx::current().id(); + + let guard_number + = Self::LockGuard::obtain_number:: + (&timepoint, Seal(())) + .unwrap_or_else(|| Self::Error::overflow().into())?; + + let guard = Self::LockGuard::new( + timepoint, + guard_number + ); + + guard.lock::(); + + Ok(guard) + } + fn hold_fraction( mut fraction: Self::FractionRecord, _: Seal @@ -659,6 +702,8 @@ pub trait FractionRecordT: Sized { fn holds(&self) -> &Impl::FractionHoldGuard; + fn lock_mask(&self) -> FractionLockMask; + fn new( account: &Impl::Account, fingerprint: Impl::Fingerprint, @@ -671,6 +716,8 @@ pub trait FractionRecordT: Sized { fn _mut_holds(&mut self) -> &mut Impl::FractionHoldGuard; + fn _mut_lock_mask(&mut self) -> &mut u8; + fn can_fuse(&self) -> bool { self.amount() == self.fractional().total() } @@ -700,6 +747,14 @@ pub trait FractionRecordT: Sized { *self._mut_holds() = self.holds().checked_sub(&One::one()).ok_or(())?; Ok(()) } + + fn lock(&mut self, mask: FractionLockMask) { + *self._mut_lock_mask() = self.lock_mask().or(mask).into(); + } + + fn unlock(&mut self, mask: FractionLockMask) { + *self._mut_lock_mask() = self.lock_mask().and(!mask).into(); + } } // Collection record: @@ -842,6 +897,7 @@ pub struct NFTokenFractionRecord FractionRecordT @@ -873,6 +929,10 @@ impl FractionRecordT &self.holds } + fn lock_mask(&self) -> FractionLockMask { + self.lock_mask.into() + } + fn new( account: &Impl::Account, fingerprint: Impl::Fingerprint, @@ -886,7 +946,8 @@ impl FractionRecordT fingerprint, fractional, amount, - holds + holds, + lock_mask: 0u8 } } @@ -897,23 +958,36 @@ impl FractionRecordT fn _mut_holds(&mut self) -> &mut Impl::FractionHoldGuard { &mut self.holds } + + fn _mut_lock_mask(&mut self) -> &mut u8 { + &mut self.lock_mask + } } // Locks: -#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Debug)] +#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, TypeInfo, Debug)] pub struct LockGuard { timepoint: (BlockNumber, ExtrinsicIndex), - number: LockNumber + number: LockNumber, + mask: u8 } -pub trait LockGuardT: Sized +pub trait LockGuardT: Sized + Copy + where + Ctx::BlockNumber: Copy, + Ctx::ExtrinsicId: Copy, { fn new( timepoint: TransactionCtxId, - number: u32 + number: u32, + mask: u8 ) -> Self; + fn block_number(&self) -> Ctx::BlockNumber; + + fn extrinsic_index(&self) -> Ctx::ExtrinsicId; + fn obtain_number: Sized ) -> Option { let number = S::try_get( - &timepoint.block_number, - &timepoint.extrinsic_id + timepoint.block_number, + timepoint.extrinsic_id ).unwrap_or_else(|| 0u32); S::put(timepoint, number.checked_add(1u32)?); Some(number) } } +pub trait FractionLockGuardT: LockGuardT +{ + fn lock< + S: StorageDoubleMap< + Impl::Fingerprint, + Impl::Account, + Impl::FractionHoldGuard + > + >(fraction: &mut Impl::FractionRecord) + { + fraction.inc_holds(); + S::insert( + fraction.fingerprint(), + fraction.account(), + fraction.holds() + ); + } + + fn unlock< + S: StorageDoubleMap< + Impl::Fingerprint, + Impl::Account, + Impl::FractionHoldGuard + > + >(fraction: &mut Impl::FractionRecord) + { + fraction.dec_holds(); + if fraction.holds().is_zero() { + S::remove( + fraction.fingerprint(), + fraction.account() + ); + } else { + S::insert( + fraction.fingerprint(), + fraction.account(), + fraction.holds() + ); + } + } +} + impl LockGuardT for LockGuard< Ctx::BlockNumber, @@ -942,7 +1058,8 @@ impl LockGuardT { fn new( timepoint: TransactionCtxId, - number: u32 + number: u32, + mask: u8 ) -> Self { let TransactionCtxId { @@ -950,7 +1067,30 @@ impl LockGuardT } = timepoint; Self { timepoint: (block_number, extrinsic_id), - number + number, + mask } } + + fn block_number(&self) -> Ctx::BlockNumber { + self.timepoint.0 + } + + fn extrinsic_index(&self) -> Ctx::ExtrinsicId { + self.timepoint.1 + } +} + +use bitmask_enum::bitmask; + +#[bitmask(u8)] +pub enum ItemLockMask { + Transfer = 0b0001, + MintFraction = 0b0010, + BurnFraction = 0b0100, +} + +#[bitmask(u8)] +pub enum FractionLockMask { + Transfer = 0b0001, }