From d2a1c7becc07aa80393a52750bcb43b8c0fb4921 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 12:24:53 +0100 Subject: [PATCH 01/48] Improve naming. --- Cargo.lock | 36 ++--- Cargo.toml | 2 +- bin/node/cli/src/chain_spec.rs | 2 +- bin/node/runtime/Cargo.toml | 8 +- bin/node/runtime/src/lib.rs | 8 +- bin/node/testing/src/genesis.rs | 2 +- frame/{gilt => nis}/Cargo.toml | 2 +- frame/{gilt => nis}/README.md | 0 frame/{gilt => nis}/src/benchmarking.rs | 0 frame/{gilt => nis}/src/lib.rs | 189 ++++++++++++------------ frame/{gilt => nis}/src/mock.rs | 6 +- frame/{gilt => nis}/src/tests.rs | 6 +- frame/{gilt => nis}/src/weights.rs | 10 +- 13 files changed, 135 insertions(+), 136 deletions(-) rename frame/{gilt => nis}/Cargo.toml (98%) rename frame/{gilt => nis}/README.md (100%) rename frame/{gilt => nis}/src/benchmarking.rs (100%) rename frame/{gilt => nis}/src/lib.rs (80%) rename frame/{gilt => nis}/src/mock.rs (97%) rename frame/{gilt => nis}/src/tests.rs (98%) rename frame/{gilt => nis}/src/weights.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index b87f45508dfc9..71bd6b0d74b04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3392,7 +3392,6 @@ dependencies = [ "pallet-election-provider-support-benchmarking", "pallet-elections-phragmen", "pallet-fast-unstake", - "pallet-gilt", "pallet-grandpa", "pallet-identity", "pallet-im-online", @@ -3401,6 +3400,7 @@ dependencies = [ "pallet-membership", "pallet-mmr", "pallet-multisig", + "pallet-nis", "pallet-nomination-pools", "pallet-nomination-pools-benchmarking", "pallet-nomination-pools-runtime-api", @@ -5575,23 +5575,6 @@ dependencies = [ "substrate-test-utils", ] -[[package]] -name = "pallet-gilt" -version = "4.0.0-dev" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-grandpa" version = "4.0.0-dev" @@ -5776,6 +5759,23 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-nis" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "parity-scale-codec", + "scale-info", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-node-authorization" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index d3c801fc2c7be..c0cbeac6a1fdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,7 +100,7 @@ members = [ "frame/examples/offchain-worker", "frame/examples/parallel", "frame/executive", - "frame/gilt", + "frame/nis", "frame/grandpa", "frame/identity", "frame/im-online", diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 8d74f2bde0f44..3a648d378c977 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -359,7 +359,7 @@ pub fn testnet_genesis( }, vesting: Default::default(), assets: Default::default(), - gilt: Default::default(), + nis: Default::default(), transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 39364961d57e2..6bf076ed26519 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -68,7 +68,7 @@ pallet-election-provider-multi-phase = { version = "4.0.0-dev", default-features pallet-election-provider-support-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../../frame/election-provider-support/benchmarking", optional = true } pallet-elections-phragmen = { version = "5.0.0-dev", default-features = false, path = "../../../frame/elections-phragmen" } pallet-fast-unstake = { version = "4.0.0-dev", default-features = false, path = "../../../frame/fast-unstake" } -pallet-gilt = { version = "4.0.0-dev", default-features = false, path = "../../../frame/gilt" } +pallet-nis = { version = "4.0.0-dev", default-features = false, path = "../../../frame/nis" } pallet-grandpa = { version = "4.0.0-dev", default-features = false, path = "../../../frame/grandpa" } pallet-im-online = { version = "4.0.0-dev", default-features = false, path = "../../../frame/im-online" } pallet-indices = { version = "4.0.0-dev", default-features = false, path = "../../../frame/indices" } @@ -143,7 +143,7 @@ std = [ "pallet-elections-phragmen/std", "pallet-fast-unstake/std", "frame-executive/std", - "pallet-gilt/std", + "pallet-nis/std", "pallet-grandpa/std", "pallet-im-online/std", "pallet-indices/std", @@ -221,7 +221,7 @@ runtime-benchmarks = [ "pallet-election-provider-support-benchmarking/runtime-benchmarks", "pallet-elections-phragmen/runtime-benchmarks", "pallet-fast-unstake/runtime-benchmarks", - "pallet-gilt/runtime-benchmarks", + "pallet-nis/runtime-benchmarks", "pallet-grandpa/runtime-benchmarks", "pallet-identity/runtime-benchmarks", "pallet-im-online/runtime-benchmarks", @@ -274,7 +274,7 @@ try-runtime = [ "pallet-election-provider-multi-phase/try-runtime", "pallet-elections-phragmen/try-runtime", "pallet-fast-unstake/try-runtime", - "pallet-gilt/try-runtime", + "pallet-nis/try-runtime", "pallet-grandpa/try-runtime", "pallet-im-online/try-runtime", "pallet-indices/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d49312bd6fe3f..31e53ca718ffb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1454,7 +1454,7 @@ parameter_types! { pub const MaxIntakeBids: u32 = 10; } -impl pallet_gilt::Config for Runtime { +impl pallet_nis::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = Balance; @@ -1469,7 +1469,7 @@ impl pallet_gilt::Config for Runtime { type MinFreeze = MinFreeze; type IntakePeriod = IntakePeriod; type MaxIntakeBids = MaxIntakeBids; - type WeightInfo = pallet_gilt::weights::SubstrateWeight; + type WeightInfo = pallet_nis::weights::SubstrateWeight; } parameter_types! { @@ -1654,7 +1654,7 @@ construct_runtime!( Assets: pallet_assets, Mmr: pallet_mmr, Lottery: pallet_lottery, - Gilt: pallet_gilt, + Gilt: pallet_nis, Uniques: pallet_uniques, TransactionStorage: pallet_transaction_storage, VoterList: pallet_bags_list::, @@ -1757,7 +1757,7 @@ mod benches { [pallet_election_provider_support_benchmarking, EPSBench::] [pallet_elections_phragmen, Elections] [pallet_fast_unstake, FastUnstake] - [pallet_gilt, Gilt] + [pallet_nis, Gilt] [pallet_grandpa, Grandpa] [pallet_identity, Identity] [pallet_im_online, ImOnline] diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 1eb7318db52da..fe00df6135bfa 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -89,7 +89,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), assets: Default::default(), - gilt: Default::default(), + nis: Default::default(), transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), diff --git a/frame/gilt/Cargo.toml b/frame/nis/Cargo.toml similarity index 98% rename from frame/gilt/Cargo.toml rename to frame/nis/Cargo.toml index 8c60c847027a3..63872a58f0ef0 100644 --- a/frame/gilt/Cargo.toml +++ b/frame/nis/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pallet-gilt" +name = "pallet-nis" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" diff --git a/frame/gilt/README.md b/frame/nis/README.md similarity index 100% rename from frame/gilt/README.md rename to frame/nis/README.md diff --git a/frame/gilt/src/benchmarking.rs b/frame/nis/src/benchmarking.rs similarity index 100% rename from frame/gilt/src/benchmarking.rs rename to frame/nis/src/benchmarking.rs diff --git a/frame/gilt/src/lib.rs b/frame/nis/src/lib.rs similarity index 80% rename from frame/gilt/src/lib.rs rename to frame/nis/src/lib.rs index 28a0f5fd56e67..750cd9b6bfc8f 100644 --- a/frame/gilt/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Gilt Pallet +//! # Non-Interactive Staking (NIS) Pallet //! A pallet allowing accounts to auction for being frozen and receive open-ended //! inflation-protection in return. //! @@ -40,20 +40,20 @@ //! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of //! `QueueCount`. The balance gets reserved. There's a minimum of `MinFreeze` to avoid dust. //! -//! Until your bid is turned into an issued gilt you can retract it instantly and the funds are +//! Until your bid is turned into an issued bond you can retract it instantly and the funds are //! unreserved. //! -//! There's a target proportion of effective total issuance (i.e. accounting for existing gilts) +//! There's a target proportion of effective total issuance (i.e. accounting for existing bonds) //! which the we attempt to have frozen at any one time. It will likely be gradually increased over //! time by governance. //! -//! As the total funds frozen under gilts drops below `FrozenFraction` of the total effective +//! As the total funds frozen under bonds drops below `FrozenFraction` of the total effective //! issuance, then bids are taken from queues, with the queue of the greatest period taking //! priority. If the item in the queue's locked amount is greater than the amount left to be -//! frozen, then it is split up into multiple bids and becomes partially frozen under gilt. +//! frozen, then it is split up into multiple bids and becomes partially frozen under bond. //! //! Once an account's balance is frozen, it remains frozen until the owner thaws the balance of the -//! account. This may happen no earlier than queue's period after the point at which the gilt is +//! account. This may happen no earlier than queue's period after the point at which the bond is //! issued. //! //! ## Suggested Values @@ -115,7 +115,7 @@ pub mod pallet { + TypeInfo + MaxEncodedLen; - /// Origin required for setting the target proportion to be under gilt. + /// Origin required for setting the target proportion to be under bond. type AdminOrigin: EnsureOrigin; /// Unbalanced handler to account for funds created (in case of a higher total issuance over @@ -127,7 +127,7 @@ pub mod pallet { type Surplus: OnUnbalanced>; /// The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get - /// the issuance by which we inflate or deflate the gilt. + /// the issuance with which we determine the thawed value of a bond. type IgnoredIssuance: Get>; /// Number of duration queues in total. This sets the maximum duration supported, which is @@ -150,16 +150,16 @@ pub mod pallet { #[pallet::constant] type Period: Get; - /// The minimum amount of funds that may be offered to freeze for a gilt. Note that this - /// does not actually limit the amount which may be frozen in a gilt since gilts may be - /// split up in order to satisfy the desired amount of funds under gilts. + /// The minimum amount of funds that may be offered to freeze for a bond. Note that this + /// does not actually limit the amount which may be frozen in a bond since bonds may be + /// split up in order to satisfy the desired amount of funds under bonds. /// /// It should be at least big enough to ensure that there is no possible storage spam attack /// or queue-filling attack. #[pallet::constant] type MinFreeze: Get>; - /// The number of blocks between consecutive attempts to issue more gilts in an effort to + /// The number of blocks between consecutive attempts to issue more bonds in an effort to /// get to the target amount to be frozen. /// /// A larger value results in fewer storage hits each block, but a slower period to get to @@ -167,9 +167,9 @@ pub mod pallet { #[pallet::constant] type IntakePeriod: Get; - /// The maximum amount of bids that can be turned into issued gilts each block. A larger + /// The maximum amount of bids that can become bonds each block. A larger /// value here means less of the block available for transactions should there be a glut of - /// bids to make into gilts to reach the target. + /// bids to make into bonds to reach the target. #[pallet::constant] type MaxIntakeBids: Get; @@ -181,37 +181,37 @@ pub mod pallet { #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); - /// A single bid on a gilt, an item of a *queue* in `Queues`. + /// A single bid on a bond, an item of a *queue* in `Queues`. #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct GiltBid { + pub struct Bid { /// The amount bid. pub amount: Balance, /// The owner of the bid. pub who: AccountId, } - /// Information representing an active gilt. + /// Information representing an active bond. #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ActiveGilt { - /// The proportion of the effective total issuance (i.e. accounting for any eventual gilt + pub struct ActiveType { + /// The proportion of the effective total issuance (i.e. accounting for any eventual bond /// expansion or contraction that may eventually be claimed). pub proportion: Perquintill, - /// The amount reserved under this gilt. + /// The amount reserved under this bond. pub amount: Balance, - /// The account to whom this gilt belongs. + /// The account to whom this bond belongs. pub who: AccountId, - /// The time after which this gilt can be redeemed for the proportional amount of balance. + /// The time after which this bond can be redeemed for the proportional amount of balance. pub expiry: BlockNumber, } - /// An index for a gilt. + /// An index for a bond. pub type ActiveIndex = u32; - /// Overall information package on the active gilts. + /// Overall information package on the active bonds. /// /// The way of determining the net issuance (i.e. after factoring in all maturing frozen funds) /// is: @@ -222,14 +222,14 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ActiveGiltsTotal { - /// The total amount of funds held in reserve for all active gilts. + pub struct ActiveTotalType { + /// The total amount of funds held in reserve for all active bonds. pub frozen: Balance, /// The proportion of funds that the `frozen` balance represents to total issuance. pub proportion: Perquintill, - /// The total number of gilts issued so far. + /// The total number of bonds issued so far. pub index: ActiveIndex, - /// The target proportion of gilts within total issuance. + /// The target proportion of bonds within total issuance. pub target: Perquintill, } @@ -242,27 +242,27 @@ pub mod pallet { pub type QueueTotals = StorageValue<_, BoundedVec<(u32, BalanceOf), T::QueueCount>, ValueQuery>; - /// The queues of bids ready to become gilts. Indexed by duration (in `Period`s). + /// The queues of bids ready to become bonds. Indexed by duration (in `Period`s). #[pallet::storage] pub type Queues = StorageMap< _, Blake2_128Concat, u32, - BoundedVec, T::AccountId>, T::MaxQueueLen>, + BoundedVec, T::AccountId>, T::MaxQueueLen>, ValueQuery, >; - /// Information relating to the gilts currently active. + /// Information relating to the bonds currently active. #[pallet::storage] - pub type ActiveTotal = StorageValue<_, ActiveGiltsTotal>, ValueQuery>; + pub type ActiveTotal = StorageValue<_, ActiveTotalType>, ValueQuery>; - /// The currently active gilts, indexed according to the order of creation. + /// The currently active bonds, indexed according to the order of creation. #[pallet::storage] pub type Active = StorageMap< _, Blake2_128Concat, ActiveIndex, - ActiveGilt< + ActiveType< BalanceOf, ::AccountId, ::BlockNumber, @@ -290,17 +290,17 @@ pub mod pallet { pub enum Event { /// A bid was successfully placed. BidPlaced { who: T::AccountId, amount: BalanceOf, duration: u32 }, - /// A bid was successfully removed (before being accepted as a gilt). + /// A bid was successfully removed (before being accepted). BidRetracted { who: T::AccountId, amount: BalanceOf, duration: u32 }, - /// A bid was accepted as a gilt. The balance may not be released until expiry. - GiltIssued { + /// A bid was accepted. The balance may not be released until expiry. + Issued { index: ActiveIndex, expiry: T::BlockNumber, who: T::AccountId, amount: BalanceOf, }, - /// An expired gilt has been thawed. - GiltThawed { + /// An expired bond has been thawed. + Thawed { index: ActiveIndex, who: T::AccountId, original_amount: BalanceOf, @@ -319,11 +319,11 @@ pub mod pallet { /// The queue for the bid's duration is full and the amount bid is too low to get in /// through replacing an existing bid. BidTooLow, - /// Gilt index is unknown. + /// Bond index is unknown. Unknown, - /// Not the owner of the gilt. + /// Not the owner of the bond. NotOwner, - /// Gilt not yet at expiry date. + /// Bond not yet at expiry date. NotExpired, /// The given bid for retraction is not found. NotFound, @@ -342,15 +342,14 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Place a bid for a gilt to be issued. + /// Place a bid for a bond. /// /// Origin must be Signed, and account must have at least `amount` in free balance. /// /// - `amount`: The amount of the bid; these funds will be reserved. If the bid is - /// successfully elevated into an issued gilt, then these funds will continue to be - /// reserved until the gilt expires. Must be at least `MinFreeze`. - /// - `duration`: The number of periods for which the funds will be locked if the gilt is - /// issued. It will expire only after this period has elapsed after the point of issuance. + /// successfully elevated into a bond, then these funds will continue to be + /// reserved until the bond thaws. Must be at least `MinFreeze`. + /// - `duration`: The number of periods before which the bond may be thawed. /// Must be greater than 1 and no more than `QueueCount`. /// /// Complexities: @@ -376,7 +375,7 @@ pub mod pallet { T::Currency::reserve(&who, amount)?; // queue is - let mut bid = GiltBid { amount, who: who.clone() }; + let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); T::Currency::unreserve(&bid.who, bid.amount); @@ -423,7 +422,7 @@ pub mod pallet { let queue_index = duration.checked_sub(1).ok_or(Error::::DurationTooSmall)? as usize; ensure!(queue_index < queue_count, Error::::DurationTooBig); - let bid = GiltBid { amount, who }; + let bid = Bid { amount, who }; let new_len = Queues::::try_mutate(duration, |q| -> Result { let pos = q.iter().position(|i| i == &bid).ok_or(Error::::NotFound)?; q.remove(pos); @@ -442,11 +441,11 @@ pub mod pallet { Ok(().into()) } - /// Set target proportion of gilt-funds. + /// Set target proportion of bonded-funds. /// /// Origin must be `AdminOrigin`. /// - /// - `target`: The target proportion of effective issued funds that should be under gilts + /// - `target`: The target proportion of effective issued funds that should be in bonds /// at any one time. #[pallet::weight(T::WeightInfo::set_target())] pub fn set_target( @@ -458,13 +457,13 @@ pub mod pallet { Ok(().into()) } - /// Remove an active but expired gilt. Reserved funds under gilt are freed and balance is - /// adjusted to ensure that the funds grow or shrink to maintain the equivalent proportion - /// of effective total issued funds. + /// Remove an active but expired bond. Reserved funds under bond are freed and balance is + /// adjusted to ensure that the newly freed amount is equivalent to the original amount in + /// terms of the proportion of effective total issued funds. /// - /// Origin must be Signed and the account must be the owner of the gilt of the given index. + /// Origin must be Signed and the account must be the owner of the bond of the given index. /// - /// - `index`: The index of the gilt to be thawed. + /// - `index`: The index of the bond to be thawed. #[pallet::weight(T::WeightInfo::thaw())] pub fn thaw( origin: OriginFor, @@ -473,11 +472,11 @@ pub mod pallet { let who = ensure_signed(origin)?; // Look for `index` - let gilt = Active::::get(index).ok_or(Error::::Unknown)?; + let bond = Active::::get(index).ok_or(Error::::Unknown)?; // If found, check the owner is `who`. - ensure!(gilt.who == who, Error::::NotOwner); + ensure!(bond.who == who, Error::::NotOwner); let now = frame_system::Pallet::::block_number(); - ensure!(now >= gilt.expiry, Error::::NotExpired); + ensure!(now >= bond.expiry, Error::::NotExpired); // Remove it Active::::remove(index); @@ -485,42 +484,42 @@ pub mod pallet { let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); ActiveTotal::::mutate(|totals| { - let nongilt_issuance = total_issuance.saturating_sub(totals.frozen); + let nonbond_issuance = total_issuance.saturating_sub(totals.frozen); let effective_issuance = - totals.proportion.left_from_one().saturating_reciprocal_mul(nongilt_issuance); - let gilt_value = gilt.proportion * effective_issuance; + totals.proportion.left_from_one().saturating_reciprocal_mul(nonbond_issuance); + let bond_value = bond.proportion * effective_issuance; - totals.frozen = totals.frozen.saturating_sub(gilt.amount); - totals.proportion = totals.proportion.saturating_sub(gilt.proportion); + totals.frozen = totals.frozen.saturating_sub(bond.amount); + totals.proportion = totals.proportion.saturating_sub(bond.proportion); // Remove or mint the additional to the amount using `Deficit`/`Surplus`. - if gilt_value > gilt.amount { + if bond_value > bond.amount { // Unreserve full amount. - T::Currency::unreserve(&gilt.who, gilt.amount); - let amount = gilt_value - gilt.amount; - let deficit = T::Currency::deposit_creating(&gilt.who, amount); + T::Currency::unreserve(&bond.who, bond.amount); + let amount = bond_value - bond.amount; + let deficit = T::Currency::deposit_creating(&bond.who, amount); T::Deficit::on_unbalanced(deficit); } else { - if gilt_value < gilt.amount { - // We take anything reserved beyond the gilt's final value. - let rest = gilt.amount - gilt_value; + if bond_value < bond.amount { + // We take anything reserved beyond the bond's final value. + let rest = bond.amount - bond_value; // `slash` might seem a little aggressive, but it's the only way to do it // in case it's locked into the staking system. - let surplus = T::Currency::slash_reserved(&gilt.who, rest).0; + let surplus = T::Currency::slash_reserved(&bond.who, rest).0; T::Surplus::on_unbalanced(surplus); } // Unreserve only its new value (less than the amount reserved). Everything // should add up, but (defensive) in case it doesn't, unreserve takes lower // priority over the funds. - let err_amt = T::Currency::unreserve(&gilt.who, gilt_value); + let err_amt = T::Currency::unreserve(&bond.who, bond_value); debug_assert!(err_amt.is_zero()); } - let e = Event::GiltThawed { + let e = Event::Thawed { index, - who: gilt.who, - original_amount: gilt.amount, - additional_amount: gilt_value, + who: bond.who, + original_amount: bond.amount, + additional_amount: bond_value, }; Self::deposit_event(e); }); @@ -531,35 +530,35 @@ pub mod pallet { /// Issuance information returned by `issuance()`. pub struct IssuanceInfo { - /// The balance held in reserve over all active gilts. + /// The balance held in reserve over all active bonds. pub reserved: Balance, - /// The issuance not held in reserve for active gilts. Together with `reserved` this sums + /// The issuance not held in reserve for active bonds. Together with `reserved` this sums /// to `Currency::total_issuance`. - pub non_gilt: Balance, + pub non_bond: Balance, /// The balance that `reserved` is effectively worth, at present. This is not issued funds /// and could be less than `reserved` (though in most cases should be greater). pub effective: Balance, } impl Pallet { - /// Get the target amount of Gilts that we're aiming for. + /// Get the target amount of bonds that we're aiming for. pub fn target() -> Perquintill { ActiveTotal::::get().target } - /// Returns information on the issuance of gilts. + /// Returns information on the issuance of bonds. pub fn issuance() -> IssuanceInfo> { let totals = ActiveTotal::::get(); let total_issuance = T::Currency::total_issuance(); - let non_gilt = total_issuance.saturating_sub(totals.frozen); - let effective = totals.proportion.left_from_one().saturating_reciprocal_mul(non_gilt); + let non_bond = total_issuance.saturating_sub(totals.frozen); + let effective = totals.proportion.left_from_one().saturating_reciprocal_mul(non_bond); - IssuanceInfo { reserved: totals.frozen, non_gilt, effective } + IssuanceInfo { reserved: totals.frozen, non_bond, effective } } - /// Attempt to enlarge our gilt-set from bids in order to satisfy our desired target amount - /// of funds frozen into gilts. + /// Attempt to enlarge our bond-set from bids in order to satisfy our desired target amount + /// of funds frozen into bonds. pub fn pursue_target(max_bids: u32) -> Weight { let totals = ActiveTotal::::get(); if totals.proportion < totals.target { @@ -567,9 +566,9 @@ pub mod pallet { let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - let nongilt_issuance = total_issuance.saturating_sub(totals.frozen); + let nonbond_issuance = total_issuance.saturating_sub(totals.frozen); let effective_issuance = - totals.proportion.left_from_one().saturating_reciprocal_mul(nongilt_issuance); + totals.proportion.left_from_one().saturating_reciprocal_mul(nonbond_issuance); let intake = missing * effective_issuance; let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids); @@ -608,7 +607,7 @@ pub mod pallet { if remaining < bid.amount { let overflow = bid.amount - remaining; bid.amount = remaining; - q.try_push(GiltBid { amount: overflow, who: bid.who.clone() }) + q.try_push(Bid { amount: overflow, who: bid.who.clone() }) .expect("just popped, so there must be space. qed"); } let amount = bid.amount; @@ -620,12 +619,12 @@ pub mod pallet { qs[queue_index].1.defensive_saturating_sub(bid.amount); // Now to activate the bid... - let nongilt_issuance = + let nonbond_issuance = total_issuance.defensive_saturating_sub(totals.frozen); let effective_issuance = totals .proportion .left_from_one() - .saturating_reciprocal_mul(nongilt_issuance); + .saturating_reciprocal_mul(nonbond_issuance); let n = amount; let d = effective_issuance; let proportion = Perquintill::from_rational(n, d); @@ -636,10 +635,10 @@ pub mod pallet { totals.proportion.defensive_saturating_add(proportion); totals.index += 1; let e = - Event::GiltIssued { index, expiry, who: who.clone(), amount }; + Event::Issued { index, expiry, who: who.clone(), amount }; Self::deposit_event(e); - let gilt = ActiveGilt { amount, proportion, who, expiry }; - Active::::insert(index, gilt); + let bond = ActiveType { amount, proportion, who, expiry }; + Active::::insert(index, bond); bids_taken += 1; diff --git a/frame/gilt/src/mock.rs b/frame/nis/src/mock.rs similarity index 97% rename from frame/gilt/src/mock.rs rename to frame/nis/src/mock.rs index e1cdf6507ef58..b074346eb823e 100644 --- a/frame/gilt/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -17,7 +17,7 @@ //! Test environment for Gilt pallet. -use crate as pallet_gilt; +use crate as pallet_nis; use frame_support::{ ord_parameter_types, parameter_types, @@ -41,7 +41,7 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Gilt: pallet_gilt::{Pallet, Call, Config, Storage, Event}, + Gilt: pallet_nis::{Pallet, Call, Config, Storage, Event}, } ); @@ -91,7 +91,7 @@ ord_parameter_types! { pub const One: u64 = 1; } -impl pallet_gilt::Config for Test { +impl pallet_nis::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = ::Balance; diff --git a/frame/gilt/src/tests.rs b/frame/nis/src/tests.rs similarity index 98% rename from frame/gilt/src/tests.rs rename to frame/nis/src/tests.rs index 2ac369dd3b8b3..4571a198d065d 100644 --- a/frame/gilt/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -462,7 +462,7 @@ fn enlargement_to_target_works() { assert_eq!(QueueTotals::::get(), vec![(1, 40), (2, 80), (2, 80)]); run_to_block(4); - // Two new gilts should have been issued to 2 & 3 for 40 each & duration of 3. + // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( Active::::get(0).unwrap(), ActiveGilt { @@ -504,7 +504,7 @@ fn enlargement_to_target_works() { ); run_to_block(6); - // Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2. + // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Active::::get(2).unwrap(), ActiveGilt { @@ -549,7 +549,7 @@ fn enlargement_to_target_works() { assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(60))); run_to_block(10); - // Two new gilts should have been issued to 1 & 2 for 40 each & duration of 2. + // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Active::::get(4).unwrap(), ActiveGilt { diff --git a/frame/gilt/src/weights.rs b/frame/nis/src/weights.rs similarity index 97% rename from frame/gilt/src/weights.rs rename to frame/nis/src/weights.rs index 15eeb7c5104cd..05449f9f36289 100644 --- a/frame/gilt/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_gilt +//! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev //! DATE: 2022-05-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` @@ -28,12 +28,12 @@ // --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_gilt +// --pallet=pallet_nis // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --template=./.maintain/frame-weight-template.hbs -// --output=./frame/gilt/src/weights.rs +// --output=./frame/nis/src/weights.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -42,7 +42,7 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use sp_std::marker::PhantomData; -/// Weight functions needed for pallet_gilt. +/// Weight functions needed for pallet_nis. pub trait WeightInfo { fn place_bid(l: u32, ) -> Weight; fn place_bid_max() -> Weight; @@ -54,7 +54,7 @@ pub trait WeightInfo { fn pursue_target_per_queue(q: u32, ) -> Weight; } -/// Weights for pallet_gilt using the Substrate node and recommended hardware. +/// Weights for pallet_nis using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Gilt Queues (r:1 w:1) From a1748dfd63802e4f3ba4e6d6d8d06ad22fc6edfc Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 12:28:47 +0100 Subject: [PATCH 02/48] More improvements to naming --- bin/node/runtime/src/lib.rs | 4 +- frame/nis/src/benchmarking.rs | 28 ++-- frame/nis/src/lib.rs | 3 +- frame/nis/src/mock.rs | 8 +- frame/nis/src/tests.rs | 247 +++++++++++++++++----------------- frame/nis/src/weights.rs | 72 +++++----- 6 files changed, 179 insertions(+), 183 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 31e53ca718ffb..c0e36c6377f71 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1654,7 +1654,7 @@ construct_runtime!( Assets: pallet_assets, Mmr: pallet_mmr, Lottery: pallet_lottery, - Gilt: pallet_nis, + Nis: pallet_nis, Uniques: pallet_uniques, TransactionStorage: pallet_transaction_storage, VoterList: pallet_bags_list::, @@ -1757,7 +1757,7 @@ mod benches { [pallet_election_provider_support_benchmarking, EPSBench::] [pallet_elections_phragmen, Elections] [pallet_fast_unstake, FastUnstake] - [pallet_nis, Gilt] + [pallet_nis, Nis] [pallet_grandpa, Grandpa] [pallet_identity, Identity] [pallet_im_online, ImOnline] diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 92ebf81854f23..4b2a58a53f332 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Benchmarks for Gilt Pallet +//! Benchmarks for NIS Pallet #![cfg(feature = "runtime-benchmarks")] @@ -30,7 +30,7 @@ use sp_arithmetic::Perquintill; use sp_runtime::traits::{Bounded, Zero}; use sp_std::prelude::*; -use crate::Pallet as Gilt; +use crate::Pallet as Nis; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -41,7 +41,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..l { - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; } }: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get() * BalanceOf::::from(2u32), 1) verify { @@ -53,7 +53,7 @@ benchmarks! { let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..T::MaxQueueLen::get() { - Gilt::::place_bid(origin.clone().into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(origin.clone().into(), T::MinFreeze::get(), 1)?; } }: place_bid(origin, T::MinFreeze::get() * BalanceOf::::from(2u32), 1) verify { @@ -68,7 +68,7 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..l { - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; } }: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get(), 1) verify { @@ -83,9 +83,9 @@ benchmarks! { thaw { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(3u32)); - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; - Gilt::::enlarge(T::MinFreeze::get() * BalanceOf::::from(2u32), 2); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::enlarge(T::MinFreeze::get() * BalanceOf::::from(2u32), 2); Active::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); }: _(RawOrigin::Signed(caller.clone()), 0) verify { @@ -93,7 +93,7 @@ benchmarks! { } pursue_target_noop { - }: { Gilt::::pursue_target(0) } + }: { Nis::::pursue_target(0) } pursue_target_per_item { // bids taken @@ -103,13 +103,13 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(b + 1)); for _ in 0..b { - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; } Call::::set_target { target: Perquintill::from_percent(100) } .dispatch_bypass_filter(T::AdminOrigin::successful_origin())?; - }: { Gilt::::pursue_target(b) } + }: { Nis::::pursue_target(b) } pursue_target_per_queue { // total queues hit @@ -119,13 +119,13 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(q + 1)); for i in 0..q { - Gilt::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), i + 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), i + 1)?; } Call::::set_target { target: Perquintill::from_percent(100) } .dispatch_bypass_filter(T::AdminOrigin::successful_origin())?; - }: { Gilt::::pursue_target(q) } + }: { Nis::::pursue_target(q) } - impl_benchmark_test_suite!(Gilt, crate::mock::new_test_ext(), crate::mock::Test); + impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 750cd9b6bfc8f..0b9852d192a8e 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -634,8 +634,7 @@ pub mod pallet { totals.proportion = totals.proportion.defensive_saturating_add(proportion); totals.index += 1; - let e = - Event::Issued { index, expiry, who: who.clone(), amount }; + let e = Event::Issued { index, expiry, who: who.clone(), amount }; Self::deposit_event(e); let bond = ActiveType { amount, proportion, who, expiry }; Active::::insert(index, bond); diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index b074346eb823e..a08777fc6d0f9 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Test environment for Gilt pallet. +//! Test environment for NIS pallet. use crate as pallet_nis; @@ -41,7 +41,7 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event}, Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Gilt: pallet_nis::{Pallet, Call, Config, Storage, Event}, + Nis: pallet_nis::{Pallet, Call, Config, Storage, Event}, } ); @@ -124,12 +124,12 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pub fn run_to_block(n: u64) { while System::block_number() < n { - Gilt::on_finalize(System::block_number()); + Nis::on_finalize(System::block_number()); Balances::on_finalize(System::block_number()); System::on_finalize(System::block_number()); System::set_block_number(System::block_number() + 1); System::on_initialize(System::block_number()); Balances::on_initialize(System::block_number()); - Gilt::on_initialize(System::block_number()); + Nis::on_initialize(System::block_number()); } } diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 4571a198d065d..c2c3b02fa9bc1 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tests for Gilt pallet. +//! Tests for NIS pallet. use super::*; use crate::{mock::*, Error}; @@ -33,7 +33,7 @@ fn basic_setup_works() { } assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 0, proportion: Perquintill::zero(), index: 0, @@ -49,12 +49,12 @@ fn set_target_works() { new_test_ext().execute_with(|| { run_to_block(1); let e = DispatchError::BadOrigin; - assert_noop!(Gilt::set_target(RuntimeOrigin::signed(2), Perquintill::from_percent(50)), e); - assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(50))); + assert_noop!(Nis::set_target(RuntimeOrigin::signed(2), Perquintill::from_percent(50)), e); + assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(50))); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 0, proportion: Perquintill::zero(), index: 0, @@ -68,21 +68,18 @@ fn set_target_works() { fn place_bid_works() { new_test_ext().execute_with(|| { run_to_block(1); + assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 1, 2), Error::::AmountTooSmall); assert_noop!( - Gilt::place_bid(RuntimeOrigin::signed(1), 1, 2), - Error::::AmountTooSmall - ); - assert_noop!( - Gilt::place_bid(RuntimeOrigin::signed(1), 101, 2), + Nis::place_bid(RuntimeOrigin::signed(1), 101, 2), BalancesError::::InsufficientBalance ); assert_noop!( - Gilt::place_bid(RuntimeOrigin::signed(1), 10, 4), + Nis::place_bid(RuntimeOrigin::signed(1), 10, 4), Error::::DurationTooBig ); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 10); - assert_eq!(Queues::::get(2), vec![GiltBid { amount: 10, who: 1 }]); + assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); }); } @@ -91,22 +88,22 @@ fn place_bid_works() { fn place_bid_queuing_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 20, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 5, 2)); - assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 15, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 20, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2)); + assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2)); assert_eq!(Balances::reserved_balance(1), 45); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 25, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2)); assert_eq!(Balances::reserved_balance(1), 60); - assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); + assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); assert_eq!( Queues::::get(2), vec![ - GiltBid { amount: 15, who: 1 }, - GiltBid { amount: 25, who: 1 }, - GiltBid { amount: 20, who: 1 }, + Bid { amount: 15, who: 1 }, + Bid { amount: 25, who: 1 }, + Bid { amount: 20, who: 1 }, ] ); assert_eq!(QueueTotals::::get(), vec![(0, 0), (3, 60), (0, 0)]); @@ -117,11 +114,11 @@ fn place_bid_queuing_works() { fn place_bid_fails_when_queue_full() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 10, 2)); - assert_noop!(Gilt::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::::BidTooLow); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(4), 10, 3)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 10, 2)); + assert_noop!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 3)); }); } @@ -129,24 +126,24 @@ fn place_bid_fails_when_queue_full() { fn multiple_place_bids_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 3)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); assert_eq!(Balances::reserved_balance(1), 40); assert_eq!(Balances::reserved_balance(2), 10); - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 10, who: 1 },]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), vec![ - GiltBid { amount: 10, who: 2 }, - GiltBid { amount: 10, who: 1 }, - GiltBid { amount: 10, who: 1 }, + Bid { amount: 10, who: 2 }, + Bid { amount: 10, who: 1 }, + Bid { amount: 10, who: 1 }, ] ); - assert_eq!(Queues::::get(3), vec![GiltBid { amount: 10, who: 1 },]); + assert_eq!(Queues::::get(3), vec![Bid { amount: 10, who: 1 },]); assert_eq!(QueueTotals::::get(), vec![(1, 10), (3, 30), (1, 10)]); }); } @@ -155,13 +152,13 @@ fn multiple_place_bids_works() { fn retract_single_item_queue_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1)); assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(1), vec![]); - assert_eq!(Queues::::get(2), vec![GiltBid { amount: 10, who: 1 }]); + assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); }); } @@ -170,18 +167,18 @@ fn retract_single_item_queue_works() { fn retract_with_other_and_duplicate_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_ok!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 20); assert_eq!(Balances::reserved_balance(2), 10); - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 10, who: 1 },]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), - vec![GiltBid { amount: 10, who: 2 }, GiltBid { amount: 10, who: 1 },] + vec![Bid { amount: 10, who: 2 }, Bid { amount: 10, who: 1 },] ); assert_eq!(QueueTotals::::get(), vec![(1, 10), (2, 20), (0, 0)]); }); @@ -191,11 +188,11 @@ fn retract_with_other_and_duplicate_works() { fn retract_non_existent_item_fails() { new_test_ext().execute_with(|| { run_to_block(1); - assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::::NotFound); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::::NotFound); - assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::::NotFound); - assert_noop!(Gilt::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::::NotFound); + assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::::NotFound); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::::NotFound); + assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::::NotFound); + assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::::NotFound); }); } @@ -203,20 +200,20 @@ fn retract_non_existent_item_fails() { fn basic_enlarge_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2)); - Gilt::enlarge(40, 2); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); + Nis::enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount assert_eq!(Balances::reserved_balance(1), 40); assert_eq!(Balances::reserved_balance(2), 40); - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 40, who: 1 }]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(Queues::::get(2), vec![]); assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 40, proportion: Perquintill::from_percent(10), index: 1, @@ -225,7 +222,7 @@ fn basic_enlarge_works() { ); assert_eq!( Active::::get(0).unwrap(), - ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } ); }); } @@ -234,21 +231,21 @@ fn basic_enlarge_works() { fn enlarge_respects_bids_limit() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 40, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(4), 40, 3)); - Gilt::enlarge(100, 2); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 40, 3)); + Nis::enlarge(100, 2); // Should have taken 4/3 and 2/2, then stopped because it's only allowed 2. - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 40, who: 1 }]); - assert_eq!(Queues::::get(2), vec![GiltBid { amount: 40, who: 3 }]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); + assert_eq!(Queues::::get(2), vec![Bid { amount: 40, who: 3 }]); assert_eq!(Queues::::get(3), vec![]); assert_eq!(QueueTotals::::get(), vec![(1, 40), (1, 40), (0, 0)]); assert_eq!( Active::::get(0).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 4, @@ -257,11 +254,11 @@ fn enlarge_respects_bids_limit() { ); assert_eq!( Active::::get(1).unwrap(), - ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } ); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -275,20 +272,20 @@ fn enlarge_respects_bids_limit() { fn enlarge_respects_amount_limit_and_will_split() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 80, 1)); - Gilt::enlarge(40, 2); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + Nis::enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 40, who: 1 }]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); assert_eq!( Active::::get(0).unwrap(), - ActiveGilt { proportion: Perquintill::from_percent(10), amount: 40, who: 1, expiry: 4 } + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 1, expiry: 4 } ); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 40, proportion: Perquintill::from_percent(10), index: 1, @@ -302,18 +299,18 @@ fn enlarge_respects_amount_limit_and_will_split() { fn basic_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - Gilt::enlarge(40, 1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + Nis::enlarge(40, 1); run_to_block(3); - assert_noop!(Gilt::thaw(RuntimeOrigin::signed(1), 0), Error::::NotExpired); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), Error::::NotExpired); run_to_block(4); - assert_noop!(Gilt::thaw(RuntimeOrigin::signed(1), 1), Error::::Unknown); - assert_noop!(Gilt::thaw(RuntimeOrigin::signed(2), 0), Error::::NotOwner); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1), Error::::Unknown); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0), Error::::NotOwner); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 0, proportion: Perquintill::zero(), index: 1, @@ -330,8 +327,8 @@ fn basic_thaw_works() { fn thaw_when_issuance_higher_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Gilt::enlarge(100, 1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + Nis::enlarge(100, 1); // Everybody else's balances goes up by 50% Balances::make_free_balance_be(&2, 150); @@ -339,7 +336,7 @@ fn thaw_when_issuance_higher_works() { Balances::make_free_balance_be(&4, 150); run_to_block(4); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); @@ -353,8 +350,8 @@ fn thaw_with_ignored_issuance_works() { // Give account zero some balance. Balances::make_free_balance_be(&0, 200); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Gilt::enlarge(100, 1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + Nis::enlarge(100, 1); // Account zero transfers 50 into everyone else's accounts. assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50)); @@ -362,7 +359,7 @@ fn thaw_with_ignored_issuance_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50)); run_to_block(4); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); // Account zero changes have been ignored. assert_eq!(Balances::free_balance(1), 150); @@ -374,8 +371,8 @@ fn thaw_with_ignored_issuance_works() { fn thaw_when_issuance_lower_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Gilt::enlarge(100, 1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + Nis::enlarge(100, 1); // Everybody else's balances goes down by 25% Balances::make_free_balance_be(&2, 75); @@ -383,7 +380,7 @@ fn thaw_when_issuance_lower_works() { Balances::make_free_balance_be(&4, 75); run_to_block(4); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!(Balances::free_balance(1), 75); assert_eq!(Balances::reserved_balance(1), 0); @@ -394,10 +391,10 @@ fn thaw_when_issuance_lower_works() { fn multiple_thaws_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 50, 1)); - Gilt::enlarge(200, 3); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + Nis::enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); @@ -405,9 +402,9 @@ fn multiple_thaws_works() { Balances::make_free_balance_be(&4, 200); run_to_block(4); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 1)); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(2), 2)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -418,10 +415,10 @@ fn multiple_thaws_works() { fn multiple_thaws_works_in_alternative_thaw_order() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 50, 1)); - Gilt::enlarge(200, 3); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + Nis::enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); @@ -429,9 +426,9 @@ fn multiple_thaws_works_in_alternative_thaw_order() { Balances::make_free_balance_be(&4, 200); run_to_block(4); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(2), 2)); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 1)); - assert_ok!(Gilt::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -442,22 +439,22 @@ fn multiple_thaws_works_in_alternative_thaw_order() { fn enlargement_to_target_works() { new_test_ext().execute_with(|| { run_to_block(2); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(1), 40, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(2), 40, 3)); - assert_ok!(Gilt::place_bid(RuntimeOrigin::signed(3), 40, 3)); - assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(40))); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 3)); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 3)); + assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(40))); run_to_block(3); - assert_eq!(Queues::::get(1), vec![GiltBid { amount: 40, who: 1 },]); + assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 },]); assert_eq!( Queues::::get(2), - vec![GiltBid { amount: 40, who: 2 }, GiltBid { amount: 40, who: 1 },] + vec![Bid { amount: 40, who: 2 }, Bid { amount: 40, who: 1 },] ); assert_eq!( Queues::::get(3), - vec![GiltBid { amount: 40, who: 3 }, GiltBid { amount: 40, who: 2 },] + vec![Bid { amount: 40, who: 3 }, Bid { amount: 40, who: 2 },] ); assert_eq!(QueueTotals::::get(), vec![(1, 40), (2, 80), (2, 80)]); @@ -465,7 +462,7 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( Active::::get(0).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, @@ -474,7 +471,7 @@ fn enlargement_to_target_works() { ); assert_eq!( Active::::get(1).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 3, @@ -483,7 +480,7 @@ fn enlargement_to_target_works() { ); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -495,7 +492,7 @@ fn enlargement_to_target_works() { // No change assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -507,7 +504,7 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Active::::get(2).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 1, @@ -516,7 +513,7 @@ fn enlargement_to_target_works() { ); assert_eq!( Active::::get(3).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, @@ -525,7 +522,7 @@ fn enlargement_to_target_works() { ); assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 160, proportion: Perquintill::from_percent(40), index: 4, @@ -537,7 +534,7 @@ fn enlargement_to_target_works() { // No change now. assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 160, proportion: Perquintill::from_percent(40), index: 4, @@ -546,13 +543,13 @@ fn enlargement_to_target_works() { ); // Set target a bit higher to use up the remaining bid. - assert_ok!(Gilt::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(60))); + assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(60))); run_to_block(10); // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Active::::get(4).unwrap(), - ActiveGilt { + ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 1, @@ -562,7 +559,7 @@ fn enlargement_to_target_works() { assert_eq!( ActiveTotal::::get(), - ActiveGiltsTotal { + ActiveTotalType { frozen: 200, proportion: Perquintill::from_percent(50), index: 5, diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 05449f9f36289..ea51f32a81a2a 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -57,8 +57,8 @@ pub trait WeightInfo { /// Weights for pallet_nis using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn place_bid(l: u32, ) -> Weight { Weight::from_ref_time(41_605_000 as u64) // Standard Error: 0 @@ -66,15 +66,15 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { Weight::from_ref_time(97_715_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn retract_bid(l: u32, ) -> Weight { Weight::from_ref_time(42_061_000 as u64) // Standard Error: 0 @@ -82,28 +82,28 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) fn set_target() -> Weight { Weight::from_ref_time(5_026_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: Gilt Active (r:1 w:1) - // Storage: Gilt ActiveTotal (r:1 w:1) + // Storage: Nis Active (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) fn thaw() -> Weight { Weight::from_ref_time(47_753_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:0) + // Storage: Nis ActiveTotal (r:1 w:0) fn pursue_target_noop() -> Weight { Weight::from_ref_time(1_663_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt Active (r:0 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis Active (r:0 w:1) fn pursue_target_per_item(b: u32, ) -> Weight { Weight::from_ref_time(40_797_000 as u64) // Standard Error: 1_000 @@ -112,10 +112,10 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(3 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(b as u64))) } - // Storage: Gilt ActiveTotal (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt Active (r:0 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis Active (r:0 w:1) fn pursue_target_per_queue(q: u32, ) -> Weight { Weight::from_ref_time(14_944_000 as u64) // Standard Error: 6_000 @@ -129,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn place_bid(l: u32, ) -> Weight { Weight::from_ref_time(41_605_000 as u64) // Standard Error: 0 @@ -138,15 +138,15 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { Weight::from_ref_time(97_715_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) fn retract_bid(l: u32, ) -> Weight { Weight::from_ref_time(42_061_000 as u64) // Standard Error: 0 @@ -154,28 +154,28 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) fn set_target() -> Weight { Weight::from_ref_time(5_026_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: Gilt Active (r:1 w:1) - // Storage: Gilt ActiveTotal (r:1 w:1) + // Storage: Nis Active (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) fn thaw() -> Weight { Weight::from_ref_time(47_753_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:0) + // Storage: Nis ActiveTotal (r:1 w:0) fn pursue_target_noop() -> Weight { Weight::from_ref_time(1_663_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) } - // Storage: Gilt ActiveTotal (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt Active (r:0 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis Active (r:0 w:1) fn pursue_target_per_item(b: u32, ) -> Weight { Weight::from_ref_time(40_797_000 as u64) // Standard Error: 1_000 @@ -184,10 +184,10 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(3 as u64)) .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(b as u64))) } - // Storage: Gilt ActiveTotal (r:1 w:1) - // Storage: Gilt QueueTotals (r:1 w:1) - // Storage: Gilt Queues (r:1 w:1) - // Storage: Gilt Active (r:0 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Nis Queues (r:1 w:1) + // Storage: Nis Active (r:0 w:1) fn pursue_target_per_queue(q: u32, ) -> Weight { Weight::from_ref_time(14_944_000 as u64) // Standard Error: 6_000 From 83946664065fa1588395aa0a4015be9ddcd6f7cf Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 14:49:52 +0100 Subject: [PATCH 03/48] Fungible counterpart --- frame/nis/src/lib.rs | 61 +++++++++++++++++++-- frame/nis/src/mock.rs | 35 +++++++++--- frame/nis/src/tests.rs | 18 +++++- frame/support/src/traits.rs | 2 +- frame/support/src/traits/tokens/fungible.rs | 2 +- 5 files changed, 99 insertions(+), 19 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 0b9852d192a8e..dffe5778e0b84 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -74,16 +74,28 @@ mod mock; mod tests; pub mod weights; +pub struct WithMaximumOf(sp_std::marker::PhantomData); +impl sp_runtime::traits::Convert for WithMaximumOf where + A::Type: Clone + sp_arithmetic::traits::Unsigned + From, + u64: TryFrom, +{ + fn convert(a: sp_runtime::Perquintill) -> A::Type { + a * A::get() + } +} + #[frame_support::pallet] pub mod pallet { pub use crate::weights::WeightInfo; use frame_support::{ pallet_prelude::*, - traits::{Currency, DefensiveSaturating, OnUnbalanced, ReservableCurrency}, + traits::{Currency, Defensive, DefensiveSaturating, OnUnbalanced, ReservableCurrency, + fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate} + }, }; use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; - use sp_runtime::traits::{Saturating, Zero}; + use sp_runtime::traits::{Saturating, Zero, Convert}; use sp_std::prelude::*; type BalanceOf = @@ -95,8 +107,33 @@ pub mod pallet { ::AccountId, >>::NegativeImbalance; + pub struct NoFungibleReceipt(sp_std::marker::PhantomData); + impl FungibleInspect for NoFungibleReceipt { + type Balance = u32; + fn total_issuance() -> u32 { 0 } + fn minimum_balance() -> u32 { 0 } + fn balance(_who: &T) -> u32 { 0 } + fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { 0 } + fn can_deposit(_who: &T, _amount: u32, _mint: bool) -> frame_support::traits::tokens::DepositConsequence { + frame_support::traits::tokens::DepositConsequence::Success + } + fn can_withdraw(_who: &T, _amount: u32) -> frame_support::traits::tokens::WithdrawConsequence { + frame_support::traits::tokens::WithdrawConsequence::Success + } + } + impl FungibleMutate for NoFungibleReceipt { + fn mint_into(_who: &T, _amount: u32) -> DispatchResult { Ok(()) } + fn burn_from(_who: &T, _amount: u32) -> Result { Ok(0) } + } + impl Convert for NoFungibleReceipt { + fn convert(_: Perquintill) -> u32 { 0 } + } + #[pallet::config] pub trait Config: frame_system::Config { + /// Information on runtime weights. + type WeightInfo: WeightInfo; + /// Overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -130,6 +167,13 @@ pub mod pallet { /// the issuance with which we determine the thawed value of a bond. type IgnoredIssuance: Get>; + type FungibleReceipt: FungibleMutate; + + type FungibleEquivalence: Convert< + Perquintill, + >::Balance, + >; + /// Number of duration queues in total. This sets the maximum duration supported, which is /// this value multiplied by `Period`. #[pallet::constant] @@ -172,9 +216,6 @@ pub mod pallet { /// bids to make into bonds to reach the target. #[pallet::constant] type MaxIntakeBids: Get; - - /// Information on runtime weights. - type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -477,6 +518,10 @@ pub mod pallet { ensure!(bond.who == who, Error::::NotOwner); let now = frame_system::Pallet::::block_number(); ensure!(now >= bond.expiry, Error::::NotExpired); + + let fung_eq = T::FungibleEquivalence::convert(bond.proportion); + T::FungibleReceipt::burn_from(&who, fung_eq)?; + // Remove it Active::::remove(index); @@ -636,9 +681,13 @@ pub mod pallet { totals.index += 1; let e = Event::Issued { index, expiry, who: who.clone(), amount }; Self::deposit_event(e); - let bond = ActiveType { amount, proportion, who, expiry }; + let bond = ActiveType { amount, proportion, who: who.clone(), expiry }; Active::::insert(index, bond); + // issue the fungible counterpart + let fung_eq = T::FungibleEquivalence::convert(proportion); + let _ = T::FungibleReceipt::mint_into(&who, fung_eq).defensive(); + bids_taken += 1; if remaining.is_zero() || bids_taken == max_bids { diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index a08777fc6d0f9..78645f35fa215 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -17,17 +17,18 @@ //! Test environment for NIS pallet. -use crate as pallet_nis; +use crate::{self as pallet_nis, NoFungibleReceipt, WithMaximumOf}; use frame_support::{ ord_parameter_types, parameter_types, - traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize}, + traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim}, }; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; +use pallet_balances::{Instance1, Instance2}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -39,9 +40,10 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Config, Storage, Event}, - Nis: pallet_nis::{Pallet, Call, Config, Storage, Event}, + System: frame_system, + Balances: pallet_balances::, + NisBalances: pallet_balances::, + Nis: pallet_nis, } ); @@ -72,7 +74,7 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } -impl pallet_balances::Config for Test { +impl pallet_balances::Config for Test { type Balance = u64; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; @@ -84,6 +86,19 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type AccountStore = + StorageMapShim, frame_system::Provider, u64, pallet_balances::AccountData>; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; +} + parameter_types! { pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored. } @@ -92,13 +107,16 @@ ord_parameter_types! { } impl pallet_nis::Config for Test { + type WeightInfo = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type CurrencyBalance = ::Balance; + type CurrencyBalance = >::Balance; type AdminOrigin = frame_system::EnsureSignedBy; type Deficit = (); type Surplus = (); type IgnoredIssuance = IgnoredIssuance; + type FungibleReceipt = NisBalances; + type FungibleEquivalence = WithMaximumOf>; type QueueCount = ConstU32<3>; type MaxQueueLen = ConstU32<3>; type FifoQueueLen = ConstU32<1>; @@ -106,14 +124,13 @@ impl pallet_nis::Config for Test { type MinFreeze = ConstU64<2>; type IntakePeriod = ConstU64<2>; type MaxIntakeBids = ConstU32<2>; - type WeightInfo = (); } // This function basically just builds a genesis storage key/value store according to // our desired mockup. pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { + pallet_balances::GenesisConfig:: { balances: vec![(1, 100), (2, 100), (3, 100), (4, 100)], } .assimilate_storage(&mut t) diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index c2c3b02fa9bc1..925be77b0f067 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -20,8 +20,9 @@ use super::*; use crate::{mock::*, Error}; use frame_support::{assert_noop, assert_ok, dispatch::DispatchError, traits::Currency}; -use pallet_balances::Error as BalancesError; +use pallet_balances::{Error as BalancesError, Instance1, Instance2}; use sp_arithmetic::Perquintill; +use sp_runtime::TokenError; #[test] fn basic_setup_works() { @@ -71,7 +72,7 @@ fn place_bid_works() { assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 1, 2), Error::::AmountTooSmall); assert_noop!( Nis::place_bid(RuntimeOrigin::signed(1), 101, 2), - BalancesError::::InsufficientBalance + BalancesError::::InsufficientBalance ); assert_noop!( Nis::place_bid(RuntimeOrigin::signed(1), 10, 4), @@ -330,12 +331,23 @@ fn thaw_when_issuance_higher_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); Nis::enlarge(100, 1); + assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) + // Everybody else's balances goes up by 50% Balances::make_free_balance_be(&2, 150); Balances::make_free_balance_be(&3, 150); Balances::make_free_balance_be(&4, 150); run_to_block(4); + + // Transfer counterpart away... + assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 250_000)); + // ...and it's not thawable. + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), TokenError::NoFunds); + + // Transfer counterpart back... + assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(2), 1, 250_000)); + // ...and it is. assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!(Balances::free_balance(1), 150); @@ -343,6 +355,8 @@ fn thaw_when_issuance_higher_works() { }); } +// TODO: Partial thawing. + #[test] fn thaw_with_ignored_issuance_works() { new_test_ext().execute_with(|| { diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 2b6c5efee10bb..2474052666a1a 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -25,7 +25,7 @@ pub use tokens::{ Currency, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, TotalIssuanceOf, VestingSchedule, }, - fungible, fungibles, + fungible, fungibles, nonfungible, nonfungibles, imbalance::{Imbalance, OnUnbalanced, SignedImbalance}, BalanceStatus, ExistenceRequirement, Locker, WithdrawReasons, }; diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs index 90aadb6d8daa6..c753c7b7a2eda 100644 --- a/frame/support/src/traits/tokens/fungible.rs +++ b/frame/support/src/traits/tokens/fungible.rs @@ -77,7 +77,7 @@ pub trait Mutate: Inspect { /// is returned and nothing is changed. If successful, the amount of tokens reduced is returned. /// /// The default implementation just uses `withdraw` along with `reducible_balance` to ensure - /// that is doesn't fail. + /// that it doesn't fail. fn slash(who: &AccountId, amount: Self::Balance) -> Result { Self::burn_from(who, Self::reducible_balance(who, false).min(amount)) } From c81e4a4c62c6dcc07099875ddf1fa3c984b4f4af Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 15:37:38 +0100 Subject: [PATCH 04/48] Shared pot instead of reserve --- frame/nis/src/lib.rs | 35 +++++++++++++++++++++++++---------- frame/nis/src/mock.rs | 5 ++++- frame/nis/src/tests.rs | 21 +++++++++++---------- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index dffe5778e0b84..cc2d3d010bde3 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -90,12 +90,13 @@ pub mod pallet { use frame_support::{ pallet_prelude::*, traits::{Currency, Defensive, DefensiveSaturating, OnUnbalanced, ReservableCurrency, - fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate} - }, + fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate}, + ExistenceRequirement::AllowDeath, + }, PalletId, }; use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; - use sp_runtime::traits::{Saturating, Zero, Convert}; + use sp_runtime::traits::{Saturating, Zero, Convert, AccountIdConversion}; use sp_std::prelude::*; type BalanceOf = @@ -216,6 +217,10 @@ pub mod pallet { /// bids to make into bonds to reach the target. #[pallet::constant] type MaxIntakeBids: Get; + + /// The treasury's pallet id, used for deriving its sovereign account ID. + #[pallet::constant] + type PalletId: Get; } #[pallet::pallet] @@ -412,14 +417,15 @@ pub mod pallet { duration, |q| -> Result<(u32, BalanceOf), DispatchError> { let queue_full = q.len() == T::MaxQueueLen::get() as usize; + // ensure!(have_reserved(q[0].amount)); ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); - T::Currency::reserve(&who, amount)?; + T::Currency::transfer(&who, &Self::account_id(), amount, AllowDeath)?; // queue is let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - T::Currency::unreserve(&bid.who, bid.amount); + let _ = T::Currency::transfer(&Self::account_id(), &bid.who, bid.amount, AllowDeath); (0, amount - bid.amount) } else { q.try_insert(0, bid).expect("verified queue was not full above. qed."); @@ -462,6 +468,7 @@ pub mod pallet { let queue_count = T::QueueCount::get() as usize; let queue_index = duration.checked_sub(1).ok_or(Error::::DurationTooSmall)? as usize; ensure!(queue_index < queue_count, Error::::DurationTooBig); + // TODO: ensure!(have_in_reserve(amount)) let bid = Bid { amount, who }; let new_len = Queues::::try_mutate(duration, |q| -> Result { @@ -476,7 +483,7 @@ pub mod pallet { qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount); }); - T::Currency::unreserve(&bid.who, bid.amount); + let _ = T::Currency::transfer(&Self::account_id(), &bid.who, bid.amount, AllowDeath); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(().into()) @@ -540,7 +547,7 @@ pub mod pallet { // Remove or mint the additional to the amount using `Deficit`/`Surplus`. if bond_value > bond.amount { // Unreserve full amount. - T::Currency::unreserve(&bond.who, bond.amount); + let _ = T::Currency::transfer(&Self::account_id(), &bond.who, bond.amount, AllowDeath).defensive(); let amount = bond_value - bond.amount; let deficit = T::Currency::deposit_creating(&bond.who, amount); T::Deficit::on_unbalanced(deficit); @@ -550,14 +557,13 @@ pub mod pallet { let rest = bond.amount - bond_value; // `slash` might seem a little aggressive, but it's the only way to do it // in case it's locked into the staking system. - let surplus = T::Currency::slash_reserved(&bond.who, rest).0; + let surplus = T::Currency::slash(&Self::account_id(), rest).0; T::Surplus::on_unbalanced(surplus); } // Unreserve only its new value (less than the amount reserved). Everything // should add up, but (defensive) in case it doesn't, unreserve takes lower // priority over the funds. - let err_amt = T::Currency::unreserve(&bond.who, bond_value); - debug_assert!(err_amt.is_zero()); + let _ = T::Currency::transfer(&Self::account_id(), &bond.who, bond_value, AllowDeath).defensive(); } let e = Event::Thawed { @@ -591,6 +597,14 @@ pub mod pallet { ActiveTotal::::get().target } + /// The account ID of the reserves. + /// + /// This actually does computation. If you need to keep using it, then make sure you cache the + /// value and only call this once. + pub fn account_id() -> T::AccountId { + T::PalletId::get().into_account_truncating() + } + /// Returns information on the issuance of bonds. pub fn issuance() -> IssuanceInfo> { let totals = ActiveTotal::::get(); @@ -656,6 +670,7 @@ pub mod pallet { .expect("just popped, so there must be space. qed"); } let amount = bid.amount; + // Can never overflow due to block above. remaining -= amount; // Should never underflow since it should track the total of the diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 78645f35fa215..c77b3189348b7 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -21,7 +21,7 @@ use crate::{self as pallet_nis, NoFungibleReceipt, WithMaximumOf}; use frame_support::{ ord_parameter_types, parameter_types, - traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim}, + traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim}, PalletId, }; use sp_core::H256; use sp_runtime::{ @@ -101,7 +101,9 @@ impl pallet_balances::Config for Test { parameter_types! { pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored. + pub const NisPalletId: PalletId = PalletId(*b"py/nis "); } + ord_parameter_types! { pub const One: u64 = 1; } @@ -124,6 +126,7 @@ impl pallet_nis::Config for Test { type MinFreeze = ConstU64<2>; type IntakePeriod = ConstU64<2>; type MaxIntakeBids = ConstU32<2>; + type PalletId = NisPalletId; } // This function basically just builds a genesis storage key/value store according to diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 925be77b0f067..367eef185638a 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -79,7 +79,7 @@ fn place_bid_works() { Error::::DurationTooBig ); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_eq!(Balances::reserved_balance(1), 10); + assert_eq!(pot(), 10); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); }); @@ -94,10 +94,10 @@ fn place_bid_queuing_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2)); assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2)); - assert_eq!(Balances::reserved_balance(1), 45); + assert_eq!(pot(), 45); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2)); - assert_eq!(Balances::reserved_balance(1), 60); + assert_eq!(pot(), 60); assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); assert_eq!( Queues::::get(2), @@ -133,8 +133,7 @@ fn multiple_place_bids_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(Balances::reserved_balance(2), 10); + assert_eq!(pot(), 50); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), @@ -157,7 +156,7 @@ fn retract_single_item_queue_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_eq!(Balances::reserved_balance(1), 10); + assert_eq!(pot(), 10); assert_eq!(Queues::::get(1), vec![]); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); @@ -174,8 +173,7 @@ fn retract_with_other_and_duplicate_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_eq!(Balances::reserved_balance(1), 20); - assert_eq!(Balances::reserved_balance(2), 10); + assert_eq!(pot(), 30); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), @@ -197,6 +195,10 @@ fn retract_non_existent_item_fails() { }); } +fn pot() -> u64 { + Balances::free_balance(&Nis::account_id()) +} + #[test] fn basic_enlarge_works() { new_test_ext().execute_with(|| { @@ -206,8 +208,7 @@ fn basic_enlarge_works() { Nis::enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount - assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(Balances::reserved_balance(2), 40); + assert_eq!(pot(), 80); assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(Queues::::get(2), vec![]); assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); From 9a781999c1327a6f35c08347964ef0f11368b691 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 15:18:33 +0000 Subject: [PATCH 05/48] Transferable receipts --- frame/nis/src/lib.rs | 32 +++++++++++++++++++++++++++++++- frame/nis/src/tests.rs | 31 ++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index cc2d3d010bde3..401b05245f22a 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -84,6 +84,8 @@ impl sp_runtime::traits::Convert = @@ -591,6 +594,33 @@ pub mod pallet { pub effective: Balance, } + impl NonfungibleInspect for Pallet { + type ItemId = ActiveIndex; + + fn owner(item: &ActiveIndex) -> Option { + Active::::get(item).map(|r| r.who) + } + + fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { + let item = Active::::get(item)?; + match key { + b"proportion" => Some(item.proportion.encode()), + b"amount" => Some(item.amount.encode()), + b"expiry" => Some(item.expiry.encode()), + _ => None, + } + } + } + + impl NonfungibleTransfer for Pallet { + fn transfer(index: &ActiveIndex, destination: &T::AccountId) -> DispatchResult { + let mut item = Active::::get(index).ok_or(TokenError::UnknownAsset)?; + item.who = destination.clone(); + Active::::insert(index, item); + Ok(()) + } + } + impl Pallet { /// Get the target amount of bonds that we're aiming for. pub fn target() -> Perquintill { diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 367eef185638a..30d6a038027cb 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -20,6 +20,7 @@ use super::*; use crate::{mock::*, Error}; use frame_support::{assert_noop, assert_ok, dispatch::DispatchError, traits::Currency}; +use frame_support::traits::nonfungible::{Inspect, Transfer}; use pallet_balances::{Error as BalancesError, Instance1, Instance2}; use sp_arithmetic::Perquintill; use sp_runtime::TokenError; @@ -321,7 +322,33 @@ fn basic_thaw_works() { ); assert_eq!(Active::::get(0), None); assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(pot(), 0); + }); +} + +#[test] +fn thaw_respects_transfers() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + Nis::enlarge(40, 1); + run_to_block(4); + + assert_eq!(Nis::owner(&0), Some(1)); + assert_ok!(Nis::transfer(&0, &2)); + + // Transfering the receipt... + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), Error::::NotOwner); + // ...can't be thawed due to missing counterpart + assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0), TokenError::NoFunds); + + // Transfer the counterpart also... + assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 2100000)); + // ...and thawing is possible. + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0)); + + assert_eq!(Balances::free_balance(2), 140); + assert_eq!(Balances::free_balance(1), 60); }); } @@ -356,8 +383,6 @@ fn thaw_when_issuance_higher_works() { }); } -// TODO: Partial thawing. - #[test] fn thaw_with_ignored_issuance_works() { new_test_ext().execute_with(|| { From 556aee9e5e107466924df6e8c01d1395f34799a3 Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 2 Nov 2022 15:21:47 +0000 Subject: [PATCH 06/48] Better naming --- frame/nis/src/lib.rs | 121 +++++++++++++++++++++++++----------- frame/nis/src/mock.rs | 18 ++++-- frame/nis/src/tests.rs | 111 +++++++++++++++++++-------------- frame/support/src/traits.rs | 4 +- 4 files changed, 165 insertions(+), 89 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 401b05245f22a..b1ffdf6bf1575 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -75,7 +75,9 @@ mod tests; pub mod weights; pub struct WithMaximumOf(sp_std::marker::PhantomData); -impl sp_runtime::traits::Convert for WithMaximumOf where +impl sp_runtime::traits::Convert + for WithMaximumOf +where A::Type: Clone + sp_arithmetic::traits::Unsigned + From, u64: TryFrom, { @@ -91,15 +93,21 @@ pub mod pallet { pub use crate::weights::WeightInfo; use frame_support::{ pallet_prelude::*, - traits::{Currency, Defensive, DefensiveSaturating, OnUnbalanced, ReservableCurrency, + traits::{ fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate}, nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, + Currency, Defensive, DefensiveSaturating, ExistenceRequirement::AllowDeath, - }, PalletId, + OnUnbalanced, ReservableCurrency, + }, + PalletId, }; use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; - use sp_runtime::{traits::{Saturating, Zero, Convert, AccountIdConversion}, TokenError}; + use sp_runtime::{ + traits::{AccountIdConversion, Convert, Saturating, Zero}, + TokenError, + }; use sp_std::prelude::*; type BalanceOf = @@ -114,23 +122,44 @@ pub mod pallet { pub struct NoFungibleReceipt(sp_std::marker::PhantomData); impl FungibleInspect for NoFungibleReceipt { type Balance = u32; - fn total_issuance() -> u32 { 0 } - fn minimum_balance() -> u32 { 0 } - fn balance(_who: &T) -> u32 { 0 } - fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { 0 } - fn can_deposit(_who: &T, _amount: u32, _mint: bool) -> frame_support::traits::tokens::DepositConsequence { + fn total_issuance() -> u32 { + 0 + } + fn minimum_balance() -> u32 { + 0 + } + fn balance(_who: &T) -> u32 { + 0 + } + fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { + 0 + } + fn can_deposit( + _who: &T, + _amount: u32, + _mint: bool, + ) -> frame_support::traits::tokens::DepositConsequence { frame_support::traits::tokens::DepositConsequence::Success } - fn can_withdraw(_who: &T, _amount: u32) -> frame_support::traits::tokens::WithdrawConsequence { + fn can_withdraw( + _who: &T, + _amount: u32, + ) -> frame_support::traits::tokens::WithdrawConsequence { frame_support::traits::tokens::WithdrawConsequence::Success } } impl FungibleMutate for NoFungibleReceipt { - fn mint_into(_who: &T, _amount: u32) -> DispatchResult { Ok(()) } - fn burn_from(_who: &T, _amount: u32) -> Result { Ok(0) } + fn mint_into(_who: &T, _amount: u32) -> DispatchResult { + Ok(()) + } + fn burn_from(_who: &T, _amount: u32) -> Result { + Ok(0) + } } impl Convert for NoFungibleReceipt { - fn convert(_: Perquintill) -> u32 { 0 } + fn convert(_: Perquintill) -> u32 { + 0 + } } #[pallet::config] @@ -245,7 +274,7 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ActiveType { + pub struct ReceiptRecord { /// The proportion of the effective total issuance (i.e. accounting for any eventual bond /// expansion or contraction that may eventually be claimed). pub proportion: Perquintill, @@ -271,7 +300,7 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ActiveTotalType { + pub struct SummaryRecord { /// The total amount of funds held in reserve for all active bonds. pub frozen: Balance, /// The proportion of funds that the `frozen` balance represents to total issuance. @@ -303,15 +332,15 @@ pub mod pallet { /// Information relating to the bonds currently active. #[pallet::storage] - pub type ActiveTotal = StorageValue<_, ActiveTotalType>, ValueQuery>; + pub type Summary = StorageValue<_, SummaryRecord>, ValueQuery>; /// The currently active bonds, indexed according to the order of creation. #[pallet::storage] - pub type Active = StorageMap< + pub type Receipts = StorageMap< _, Blake2_128Concat, ActiveIndex, - ActiveType< + ReceiptRecord< BalanceOf, ::AccountId, ::BlockNumber, @@ -428,7 +457,12 @@ pub mod pallet { let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - let _ = T::Currency::transfer(&Self::account_id(), &bid.who, bid.amount, AllowDeath); + let _ = T::Currency::transfer( + &Self::account_id(), + &bid.who, + bid.amount, + AllowDeath, + ); (0, amount - bid.amount) } else { q.try_insert(0, bid).expect("verified queue was not full above. qed."); @@ -504,7 +538,7 @@ pub mod pallet { #[pallet::compact] target: Perquintill, ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin(origin)?; - ActiveTotal::::mutate(|totals| totals.target = target); + Summary::::mutate(|totals| totals.target = target); Ok(().into()) } @@ -523,7 +557,7 @@ pub mod pallet { let who = ensure_signed(origin)?; // Look for `index` - let bond = Active::::get(index).ok_or(Error::::Unknown)?; + let bond = Receipts::::get(index).ok_or(Error::::Unknown)?; // If found, check the owner is `who`. ensure!(bond.who == who, Error::::NotOwner); let now = frame_system::Pallet::::block_number(); @@ -533,12 +567,12 @@ pub mod pallet { T::FungibleReceipt::burn_from(&who, fung_eq)?; // Remove it - Active::::remove(index); + Receipts::::remove(index); // Multiply the proportion it is by the total issued. let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - ActiveTotal::::mutate(|totals| { + Summary::::mutate(|totals| { let nonbond_issuance = total_issuance.saturating_sub(totals.frozen); let effective_issuance = totals.proportion.left_from_one().saturating_reciprocal_mul(nonbond_issuance); @@ -550,7 +584,13 @@ pub mod pallet { // Remove or mint the additional to the amount using `Deficit`/`Surplus`. if bond_value > bond.amount { // Unreserve full amount. - let _ = T::Currency::transfer(&Self::account_id(), &bond.who, bond.amount, AllowDeath).defensive(); + let _ = T::Currency::transfer( + &Self::account_id(), + &bond.who, + bond.amount, + AllowDeath, + ) + .defensive(); let amount = bond_value - bond.amount; let deficit = T::Currency::deposit_creating(&bond.who, amount); T::Deficit::on_unbalanced(deficit); @@ -566,7 +606,13 @@ pub mod pallet { // Unreserve only its new value (less than the amount reserved). Everything // should add up, but (defensive) in case it doesn't, unreserve takes lower // priority over the funds. - let _ = T::Currency::transfer(&Self::account_id(), &bond.who, bond_value, AllowDeath).defensive(); + let _ = T::Currency::transfer( + &Self::account_id(), + &bond.who, + bond_value, + AllowDeath, + ) + .defensive(); } let e = Event::Thawed { @@ -598,11 +644,11 @@ pub mod pallet { type ItemId = ActiveIndex; fn owner(item: &ActiveIndex) -> Option { - Active::::get(item).map(|r| r.who) + Receipts::::get(item).map(|r| r.who) } fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { - let item = Active::::get(item)?; + let item = Receipts::::get(item)?; match key { b"proportion" => Some(item.proportion.encode()), b"amount" => Some(item.amount.encode()), @@ -614,9 +660,9 @@ pub mod pallet { impl NonfungibleTransfer for Pallet { fn transfer(index: &ActiveIndex, destination: &T::AccountId) -> DispatchResult { - let mut item = Active::::get(index).ok_or(TokenError::UnknownAsset)?; + let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; item.who = destination.clone(); - Active::::insert(index, item); + Receipts::::insert(index, item); Ok(()) } } @@ -624,20 +670,20 @@ pub mod pallet { impl Pallet { /// Get the target amount of bonds that we're aiming for. pub fn target() -> Perquintill { - ActiveTotal::::get().target + Summary::::get().target } /// The account ID of the reserves. /// - /// This actually does computation. If you need to keep using it, then make sure you cache the - /// value and only call this once. + /// This actually does computation. If you need to keep using it, then make sure you cache + /// the value and only call this once. pub fn account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } /// Returns information on the issuance of bonds. pub fn issuance() -> IssuanceInfo> { - let totals = ActiveTotal::::get(); + let totals = Summary::::get(); let total_issuance = T::Currency::total_issuance(); let non_bond = total_issuance.saturating_sub(totals.frozen); @@ -649,7 +695,7 @@ pub mod pallet { /// Attempt to enlarge our bond-set from bids in order to satisfy our desired target amount /// of funds frozen into bonds. pub fn pursue_target(max_bids: u32) -> Weight { - let totals = ActiveTotal::::get(); + let totals = Summary::::get(); if totals.proportion < totals.target { let missing = totals.target.saturating_sub(totals.proportion); @@ -682,7 +728,7 @@ pub mod pallet { let mut queues_hit = 0; let now = frame_system::Pallet::::block_number(); - ActiveTotal::::mutate(|totals| { + Summary::::mutate(|totals| { QueueTotals::::mutate(|qs| { for duration in (1..=T::QueueCount::get()).rev() { if qs[duration as usize - 1].0 == 0 { @@ -726,8 +772,9 @@ pub mod pallet { totals.index += 1; let e = Event::Issued { index, expiry, who: who.clone(), amount }; Self::deposit_event(e); - let bond = ActiveType { amount, proportion, who: who.clone(), expiry }; - Active::::insert(index, bond); + let bond = + ReceiptRecord { amount, proportion, who: who.clone(), expiry }; + Receipts::::insert(index, bond); // issue the fungible counterpart let fung_eq = T::FungibleEquivalence::convert(proportion); diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index c77b3189348b7..5a8f6f0993848 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -17,18 +17,22 @@ //! Test environment for NIS pallet. -use crate::{self as pallet_nis, NoFungibleReceipt, WithMaximumOf}; +use crate::{self as pallet_nis, WithMaximumOf}; use frame_support::{ ord_parameter_types, parameter_types, - traits::{ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim}, PalletId, + traits::{ + ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, + StorageMapShim, + }, + PalletId, }; +use pallet_balances::{Instance1, Instance2}; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -use pallet_balances::{Instance1, Instance2}; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; @@ -91,8 +95,12 @@ impl pallet_balances::Config for Test { type DustRemoval = (); type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = frame_support::traits::ConstU64<1>; - type AccountStore = - StorageMapShim, frame_system::Provider, u64, pallet_balances::AccountData>; + type AccountStore = StorageMapShim< + pallet_balances::Account, + frame_system::Provider, + u64, + pallet_balances::AccountData, + >; type WeightInfo = (); type MaxLocks = (); type MaxReserves = (); diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 30d6a038027cb..784de932b21e5 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -19,9 +19,15 @@ use super::*; use crate::{mock::*, Error}; -use frame_support::{assert_noop, assert_ok, dispatch::DispatchError, traits::Currency}; -use frame_support::traits::nonfungible::{Inspect, Transfer}; -use pallet_balances::{Error as BalancesError, Instance1, Instance2}; +use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchError, + traits::{ + nonfungible::{Inspect, Transfer}, + Currency, + }, +}; +use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; use sp_runtime::TokenError; @@ -34,8 +40,8 @@ fn basic_setup_works() { assert!(Queues::::get(q).is_empty()); } assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 0, proportion: Perquintill::zero(), index: 0, @@ -55,8 +61,8 @@ fn set_target_works() { assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(50))); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 0, proportion: Perquintill::zero(), index: 0, @@ -215,8 +221,8 @@ fn basic_enlarge_works() { assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 40, proportion: Perquintill::from_percent(10), index: 1, @@ -224,8 +230,13 @@ fn basic_enlarge_works() { } ); assert_eq!( - Active::::get(0).unwrap(), - ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } + Receipts::::get(0).unwrap(), + ReceiptRecord { + proportion: Perquintill::from_percent(10), + amount: 40, + who: 2, + expiry: 7 + } ); }); } @@ -247,8 +258,8 @@ fn enlarge_respects_bids_limit() { assert_eq!(QueueTotals::::get(), vec![(1, 40), (1, 40), (0, 0)]); assert_eq!( - Active::::get(0).unwrap(), - ActiveType { + Receipts::::get(0).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 4, @@ -256,12 +267,17 @@ fn enlarge_respects_bids_limit() { } ); assert_eq!( - Active::::get(1).unwrap(), - ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 2, expiry: 7 } + Receipts::::get(1).unwrap(), + ReceiptRecord { + proportion: Perquintill::from_percent(10), + amount: 40, + who: 2, + expiry: 7 + } ); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -283,12 +299,17 @@ fn enlarge_respects_amount_limit_and_will_split() { assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); assert_eq!( - Active::::get(0).unwrap(), - ActiveType { proportion: Perquintill::from_percent(10), amount: 40, who: 1, expiry: 4 } + Receipts::::get(0).unwrap(), + ReceiptRecord { + proportion: Perquintill::from_percent(10), + amount: 40, + who: 1, + expiry: 4 + } ); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 40, proportion: Perquintill::from_percent(10), index: 1, @@ -312,15 +333,15 @@ fn basic_thaw_works() { assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 0, proportion: Perquintill::zero(), index: 1, target: Perquintill::zero(), } ); - assert_eq!(Active::::get(0), None); + assert_eq!(Receipts::::get(0), None); assert_eq!(Balances::free_balance(1), 100); assert_eq!(pot(), 0); }); @@ -359,7 +380,7 @@ fn thaw_when_issuance_higher_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); Nis::enlarge(100, 1); - assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) + assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) // Everybody else's balances goes up by 50% Balances::make_free_balance_be(&2, 150); @@ -501,8 +522,8 @@ fn enlargement_to_target_works() { run_to_block(4); // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( - Active::::get(0).unwrap(), - ActiveType { + Receipts::::get(0).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 2, @@ -510,8 +531,8 @@ fn enlargement_to_target_works() { } ); assert_eq!( - Active::::get(1).unwrap(), - ActiveType { + Receipts::::get(1).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 3, @@ -519,8 +540,8 @@ fn enlargement_to_target_works() { } ); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -531,8 +552,8 @@ fn enlargement_to_target_works() { run_to_block(5); // No change assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 80, proportion: Perquintill::from_percent(20), index: 2, @@ -543,8 +564,8 @@ fn enlargement_to_target_works() { run_to_block(6); // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( - Active::::get(2).unwrap(), - ActiveType { + Receipts::::get(2).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 1, @@ -552,8 +573,8 @@ fn enlargement_to_target_works() { } ); assert_eq!( - Active::::get(3).unwrap(), - ActiveType { + Receipts::::get(3).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 2, @@ -561,8 +582,8 @@ fn enlargement_to_target_works() { } ); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 160, proportion: Perquintill::from_percent(40), index: 4, @@ -573,8 +594,8 @@ fn enlargement_to_target_works() { run_to_block(8); // No change now. assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 160, proportion: Perquintill::from_percent(40), index: 4, @@ -588,8 +609,8 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( - Active::::get(4).unwrap(), - ActiveType { + Receipts::::get(4).unwrap(), + ReceiptRecord { proportion: Perquintill::from_percent(10), amount: 40, who: 1, @@ -598,8 +619,8 @@ fn enlargement_to_target_works() { ); assert_eq!( - ActiveTotal::::get(), - ActiveTotalType { + Summary::::get(), + SummaryRecord { frozen: 200, proportion: Perquintill::from_percent(50), index: 5, diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 2474052666a1a..e4c7c01909df2 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -25,9 +25,9 @@ pub use tokens::{ Currency, LockIdentifier, LockableCurrency, NamedReservableCurrency, ReservableCurrency, TotalIssuanceOf, VestingSchedule, }, - fungible, fungibles, nonfungible, nonfungibles, + fungible, fungibles, imbalance::{Imbalance, OnUnbalanced, SignedImbalance}, - BalanceStatus, ExistenceRequirement, Locker, WithdrawReasons, + nonfungible, nonfungibles, BalanceStatus, ExistenceRequirement, Locker, WithdrawReasons, }; mod members; From 326bed58c363f591f8f655f3446928c51ccda163 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 08:13:36 +0000 Subject: [PATCH 07/48] Use u128 for counterpart --- frame/nis/src/lib.rs | 22 +++++++++++----------- frame/nis/src/mock.rs | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index b1ffdf6bf1575..a39b470e817dd 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -119,8 +119,8 @@ pub mod pallet { ::AccountId, >>::NegativeImbalance; - pub struct NoFungibleReceipt(sp_std::marker::PhantomData); - impl FungibleInspect for NoFungibleReceipt { + pub struct NoCounterpart(sp_std::marker::PhantomData); + impl FungibleInspect for NoCounterpart { type Balance = u32; fn total_issuance() -> u32 { 0 @@ -148,7 +148,7 @@ pub mod pallet { frame_support::traits::tokens::WithdrawConsequence::Success } } - impl FungibleMutate for NoFungibleReceipt { + impl FungibleMutate for NoCounterpart { fn mint_into(_who: &T, _amount: u32) -> DispatchResult { Ok(()) } @@ -156,7 +156,7 @@ pub mod pallet { Ok(0) } } - impl Convert for NoFungibleReceipt { + impl Convert for NoCounterpart { fn convert(_: Perquintill) -> u32 { 0 } @@ -200,11 +200,11 @@ pub mod pallet { /// the issuance with which we determine the thawed value of a bond. type IgnoredIssuance: Get>; - type FungibleReceipt: FungibleMutate; + type Counterpart: FungibleMutate; - type FungibleEquivalence: Convert< + type CounterpartAmount: Convert< Perquintill, - >::Balance, + >::Balance, >; /// Number of duration queues in total. This sets the maximum duration supported, which is @@ -563,8 +563,8 @@ pub mod pallet { let now = frame_system::Pallet::::block_number(); ensure!(now >= bond.expiry, Error::::NotExpired); - let fung_eq = T::FungibleEquivalence::convert(bond.proportion); - T::FungibleReceipt::burn_from(&who, fung_eq)?; + let fung_eq = T::CounterpartAmount::convert(bond.proportion); + T::Counterpart::burn_from(&who, fung_eq)?; // Remove it Receipts::::remove(index); @@ -777,8 +777,8 @@ pub mod pallet { Receipts::::insert(index, bond); // issue the fungible counterpart - let fung_eq = T::FungibleEquivalence::convert(proportion); - let _ = T::FungibleReceipt::mint_into(&who, fung_eq).defensive(); + let fung_eq = T::CounterpartAmount::convert(proportion); + let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); bids_taken += 1; diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 5a8f6f0993848..f1ebeb465eeca 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -28,7 +28,7 @@ use frame_support::{ PalletId, }; use pallet_balances::{Instance1, Instance2}; -use sp_core::H256; +use sp_core::{ConstU128, H256}; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, @@ -91,15 +91,15 @@ impl pallet_balances::Config for Test { } impl pallet_balances::Config for Test { - type Balance = u64; + type Balance = u128; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = frame_support::traits::ConstU64<1>; + type ExistentialDeposit = frame_support::traits::ConstU128<1>; type AccountStore = StorageMapShim< pallet_balances::Account, frame_system::Provider, u64, - pallet_balances::AccountData, + pallet_balances::AccountData, >; type WeightInfo = (); type MaxLocks = (); @@ -125,8 +125,8 @@ impl pallet_nis::Config for Test { type Deficit = (); type Surplus = (); type IgnoredIssuance = IgnoredIssuance; - type FungibleReceipt = NisBalances; - type FungibleEquivalence = WithMaximumOf>; + type Counterpart = NisBalances; + type CounterpartAmount = WithMaximumOf>; type QueueCount = ConstU32<3>; type MaxQueueLen = ConstU32<3>; type FifoQueueLen = ConstU32<1>; From fb9857fc35a63b8b99a57988a48ecc1807dcd4a0 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 14:15:30 +0000 Subject: [PATCH 08/48] Partial thawing --- frame/nis/src/lib.rs | 406 ++++++++++++------------ frame/nis/src/mock.rs | 2 +- frame/nis/src/tests.rs | 205 ++++++------ frame/nis/src/weights.rs | 15 + frame/support/src/traits/misc.rs | 30 +- primitives/arithmetic/src/lib.rs | 4 +- primitives/arithmetic/src/per_things.rs | 194 +++++------ primitives/runtime/src/traits.rs | 11 + 8 files changed, 443 insertions(+), 424 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index a39b470e817dd..8a3a9996363f9 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -66,6 +66,12 @@ #![cfg_attr(not(feature = "std"), no_std)] pub use pallet::*; +use sp_arithmetic::{traits::Unsigned, RationalArg}; +use sp_core::TypedGet; +use sp_runtime::{ + traits::{Convert, ConvertBack}, + Perquintill, +}; mod benchmarking; #[cfg(test)] @@ -74,19 +80,26 @@ mod mock; mod tests; pub mod weights; -pub struct WithMaximumOf(sp_std::marker::PhantomData); -impl sp_runtime::traits::Convert - for WithMaximumOf +pub struct WithMaximumOf(sp_std::marker::PhantomData); +impl Convert for WithMaximumOf where - A::Type: Clone + sp_arithmetic::traits::Unsigned + From, + A::Type: Clone + Unsigned + From, u64: TryFrom, { - fn convert(a: sp_runtime::Perquintill) -> A::Type { + fn convert(a: Perquintill) -> A::Type { a * A::get() } } - -// TODO: Partial thawing. +impl ConvertBack for WithMaximumOf +where + A::Type: RationalArg + From, + u64: TryFrom, + u128: TryFrom, +{ + fn convert_back(a: A::Type) -> Perquintill { + Perquintill::from_rational(a, A::get()) + } +} #[frame_support::pallet] pub mod pallet { @@ -105,7 +118,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, Saturating, Zero}, + traits::{AccountIdConversion, Convert, ConvertBack, Saturating, Zero}, TokenError, }; use sp_std::prelude::*; @@ -115,9 +128,10 @@ pub mod pallet { type PositiveImbalanceOf = <::Currency as Currency< ::AccountId, >>::PositiveImbalance; - type NegativeImbalanceOf = <::Currency as Currency< + type ReceiptRecordOf = ReceiptRecord< ::AccountId, - >>::NegativeImbalance; + ::BlockNumber, + >; pub struct NoCounterpart(sp_std::marker::PhantomData); impl FungibleInspect for NoCounterpart { @@ -188,13 +202,8 @@ pub mod pallet { /// Origin required for setting the target proportion to be under bond. type AdminOrigin: EnsureOrigin; - /// Unbalanced handler to account for funds created (in case of a higher total issuance over - /// freezing period). - type Deficit: OnUnbalanced>; - - /// Unbalanced handler to account for funds destroyed (in case of a lower total issuance - /// over freezing period). - type Surplus: OnUnbalanced>; + /// Origin required for auto-funding the deficit. + type FundOrigin: EnsureOrigin; /// The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get /// the issuance with which we determine the thawed value of a bond. @@ -202,11 +211,15 @@ pub mod pallet { type Counterpart: FungibleMutate; - type CounterpartAmount: Convert< + type CounterpartAmount: ConvertBack< Perquintill, >::Balance, >; + /// Unbalanced handler to account for funds created (in case of a higher total issuance over + /// freezing period). + type Deficit: OnUnbalanced>; + /// Number of duration queues in total. This sets the maximum duration supported, which is /// this value multiplied by `Period`. #[pallet::constant] @@ -274,12 +287,10 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ReceiptRecord { + pub struct ReceiptRecord { /// The proportion of the effective total issuance (i.e. accounting for any eventual bond /// expansion or contraction that may eventually be claimed). pub proportion: Perquintill, - /// The amount reserved under this bond. - pub amount: Balance, /// The account to whom this bond belongs. pub who: AccountId, /// The time after which this bond can be redeemed for the proportional amount of balance. @@ -300,11 +311,9 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct SummaryRecord { - /// The total amount of funds held in reserve for all active bonds. - pub frozen: Balance, + pub struct SummaryRecord { /// The proportion of funds that the `frozen` balance represents to total issuance. - pub proportion: Perquintill, + pub proportion_owed: Perquintill, /// The total number of bonds issued so far. pub index: ActiveIndex, /// The target proportion of bonds within total issuance. @@ -332,21 +341,12 @@ pub mod pallet { /// Information relating to the bonds currently active. #[pallet::storage] - pub type Summary = StorageValue<_, SummaryRecord>, ValueQuery>; + pub type Summary = StorageValue<_, SummaryRecord, ValueQuery>; /// The currently active bonds, indexed according to the order of creation. #[pallet::storage] - pub type Receipts = StorageMap< - _, - Blake2_128Concat, - ActiveIndex, - ReceiptRecord< - BalanceOf, - ::AccountId, - ::BlockNumber, - >, - OptionQuery, - >; + pub type Receipts = + StorageMap<_, Blake2_128Concat, ActiveIndex, ReceiptRecordOf, OptionQuery>; #[pallet::genesis_config] #[derive(Default)] @@ -372,17 +372,29 @@ pub mod pallet { BidRetracted { who: T::AccountId, amount: BalanceOf, duration: u32 }, /// A bid was accepted. The balance may not be released until expiry. Issued { + /// The identity of the receipt. index: ActiveIndex, + /// The block number at which the receipt may be thawed. expiry: T::BlockNumber, + /// The owner of the receipt. who: T::AccountId, + /// The proportion of the effective total issuance which the receipt represents. + proportion: Perquintill, + /// The amount of funds which were debited from the owner. amount: BalanceOf, }, /// An expired bond has been thawed. Thawed { + /// The identity of the receipt. index: ActiveIndex, + /// The owner. who: T::AccountId, - original_amount: BalanceOf, - additional_amount: BalanceOf, + /// The proportion of the effective total issuance by which the owner was debited. + proportion: Perquintill, + /// The amount by which the owner was credited. + amount: BalanceOf, + /// If `true` then the receipt is done. + dropped: bool, }, } @@ -405,6 +417,12 @@ pub mod pallet { NotExpired, /// The given bid for retraction is not found. NotFound, + /// The portion supplied is beyond the value of the bond. + TooMuch, + /// Not enough funds are held to pay out. + Unfunded, + /// There are enough funds for what is required. + Funded, } #[pallet::hooks] @@ -451,18 +469,13 @@ pub mod pallet { let queue_full = q.len() == T::MaxQueueLen::get() as usize; // ensure!(have_reserved(q[0].amount)); ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); - T::Currency::transfer(&who, &Self::account_id(), amount, AllowDeath)?; + T::Currency::reserve(&who, amount)?; // queue is let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - let _ = T::Currency::transfer( - &Self::account_id(), - &bid.who, - bid.amount, - AllowDeath, - ); + let _ = T::Currency::unreserve(&bid.who, bid.amount); (0, amount - bid.amount) } else { q.try_insert(0, bid).expect("verified queue was not full above. qed."); @@ -520,7 +533,7 @@ pub mod pallet { qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount); }); - let _ = T::Currency::transfer(&Self::account_id(), &bid.who, bid.amount, AllowDeath); + let _ = T::Currency::unreserve(&bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(().into()) @@ -542,6 +555,21 @@ pub mod pallet { Ok(().into()) } + /// Ensure we have sufficient funding for all potential payouts. + /// + /// - `origin`: Must be accepted by `FundOrigin`. + #[pallet::weight(T::WeightInfo::fund_deficit())] + pub fn fund_deficit(origin: OriginFor) -> DispatchResult { + T::FundOrigin::ensure_origin(origin)?; + let summary: SummaryRecord = Summary::::get(); + let our_account = Self::account_id(); + let issuance = Self::issuance_with(&our_account, &summary); + let deficit = issuance.required.saturating_sub(issuance.holdings); + ensure!(!deficit.is_zero(), Error::::Funded); + T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit)); + Ok(()) + } + /// Remove an active but expired bond. Reserved funds under bond are freed and balance is /// adjusted to ensure that the newly freed amount is equivalent to the original amount in /// terms of the proportion of effective total issued funds. @@ -553,91 +581,68 @@ pub mod pallet { pub fn thaw( origin: OriginFor, #[pallet::compact] index: ActiveIndex, - ) -> DispatchResultWithPostInfo { + portion: Option<>::Balance>, + ) -> DispatchResult { let who = ensure_signed(origin)?; // Look for `index` - let bond = Receipts::::get(index).ok_or(Error::::Unknown)?; + let mut receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::Unknown)?; // If found, check the owner is `who`. - ensure!(bond.who == who, Error::::NotOwner); + ensure!(receipt.who == who, Error::::NotOwner); let now = frame_system::Pallet::::block_number(); - ensure!(now >= bond.expiry, Error::::NotExpired); + ensure!(now >= receipt.expiry, Error::::NotExpired); - let fung_eq = T::CounterpartAmount::convert(bond.proportion); - T::Counterpart::burn_from(&who, fung_eq)?; - - // Remove it - Receipts::::remove(index); + let proportion = if let Some(counterpart) = portion { + let proportion = T::CounterpartAmount::convert_back(counterpart); + ensure!(proportion <= receipt.proportion, Error::::TooMuch); + // TODO: check if `proportion` is below a minimum bond size and if so then fail. + proportion + } else { + let fung_eq = T::CounterpartAmount::convert(receipt.proportion); + T::Counterpart::burn_from(&who, fung_eq)?; + receipt.proportion + }; // Multiply the proportion it is by the total issued. - let total_issuance = - T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - Summary::::mutate(|totals| { - let nonbond_issuance = total_issuance.saturating_sub(totals.frozen); - let effective_issuance = - totals.proportion.left_from_one().saturating_reciprocal_mul(nonbond_issuance); - let bond_value = bond.proportion * effective_issuance; - - totals.frozen = totals.frozen.saturating_sub(bond.amount); - totals.proportion = totals.proportion.saturating_sub(bond.proportion); - - // Remove or mint the additional to the amount using `Deficit`/`Surplus`. - if bond_value > bond.amount { - // Unreserve full amount. - let _ = T::Currency::transfer( - &Self::account_id(), - &bond.who, - bond.amount, - AllowDeath, - ) - .defensive(); - let amount = bond_value - bond.amount; - let deficit = T::Currency::deposit_creating(&bond.who, amount); - T::Deficit::on_unbalanced(deficit); - } else { - if bond_value < bond.amount { - // We take anything reserved beyond the bond's final value. - let rest = bond.amount - bond_value; - // `slash` might seem a little aggressive, but it's the only way to do it - // in case it's locked into the staking system. - let surplus = T::Currency::slash(&Self::account_id(), rest).0; - T::Surplus::on_unbalanced(surplus); - } - // Unreserve only its new value (less than the amount reserved). Everything - // should add up, but (defensive) in case it doesn't, unreserve takes lower - // priority over the funds. - let _ = T::Currency::transfer( - &Self::account_id(), - &bond.who, - bond_value, - AllowDeath, - ) - .defensive(); - } + let mut summary: SummaryRecord = Summary::::get(); + let our_account = Self::account_id(); + let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + let amount = proportion * effective_issuance; - let e = Event::Thawed { - index, - who: bond.who, - original_amount: bond.amount, - additional_amount: bond_value, - }; - Self::deposit_event(e); - }); + receipt.proportion.saturating_reduce(proportion); + summary.proportion_owed.saturating_reduce(proportion); - Ok(().into()) + T::Currency::transfer(&our_account, &who, amount, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + + let dropped = receipt.proportion.is_zero(); + if dropped { + Receipts::::remove(index); + } else { + Receipts::::insert(index, &receipt); + } + Summary::::put(&summary); + + Self::deposit_event(Event::Thawed { index, who, amount, proportion, dropped }); + + Ok(()) } } /// Issuance information returned by `issuance()`. pub struct IssuanceInfo { /// The balance held in reserve over all active bonds. - pub reserved: Balance, + pub holdings: Balance, /// The issuance not held in reserve for active bonds. Together with `reserved` this sums /// to `Currency::total_issuance`. - pub non_bond: Balance, + pub other: Balance, /// The balance that `reserved` is effectively worth, at present. This is not issued funds /// and could be less than `reserved` (though in most cases should be greater). pub effective: Balance, + /// The balance that `reserved` is effectively worth, at present. This is not issued funds + /// and could be less than `reserved` (though in most cases should be greater). + pub required: Balance, } impl NonfungibleInspect for Pallet { @@ -651,7 +656,6 @@ pub mod pallet { let item = Receipts::::get(item)?; match key { b"proportion" => Some(item.proportion.encode()), - b"amount" => Some(item.amount.encode()), b"expiry" => Some(item.expiry.encode()), _ => None, } @@ -682,29 +686,38 @@ pub mod pallet { } /// Returns information on the issuance of bonds. + /// + /// Also provides the summary and this pallet's account ID. pub fn issuance() -> IssuanceInfo> { - let totals = Summary::::get(); - - let total_issuance = T::Currency::total_issuance(); - let non_bond = total_issuance.saturating_sub(totals.frozen); - let effective = totals.proportion.left_from_one().saturating_reciprocal_mul(non_bond); + Self::issuance_with(&Self::account_id(), &Summary::::get()) + } - IssuanceInfo { reserved: totals.frozen, non_bond, effective } + /// Returns information on the issuance of bonds. + /// + /// Also provides the summary and this pallet's account ID. + pub fn issuance_with( + our_account: &T::AccountId, + summary: &SummaryRecord, + ) -> IssuanceInfo> { + let total_issuance = + T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); + let holdings = T::Currency::free_balance(our_account); + let other = total_issuance.saturating_sub(holdings); + let effective = + summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other); + let required = summary.proportion_owed * effective; + IssuanceInfo { holdings, other, effective, required } } /// Attempt to enlarge our bond-set from bids in order to satisfy our desired target amount /// of funds frozen into bonds. pub fn pursue_target(max_bids: u32) -> Weight { - let totals = Summary::::get(); - if totals.proportion < totals.target { - let missing = totals.target.saturating_sub(totals.proportion); + let summary: SummaryRecord = Summary::::get(); + if summary.proportion_owed < summary.target { + let missing = summary.target.saturating_sub(summary.proportion_owed); + let issuance = Self::issuance_with(&Self::account_id(), &summary); - let total_issuance = - T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - let nonbond_issuance = total_issuance.saturating_sub(totals.frozen); - let effective_issuance = - totals.proportion.left_from_one().saturating_reciprocal_mul(nonbond_issuance); - let intake = missing * effective_issuance; + let intake = missing * issuance.effective; let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids); let first_from_each_queue = T::WeightInfo::pursue_target_per_queue(queues_hit); @@ -721,80 +734,83 @@ pub mod pallet { /// /// Return the number of bids taken and the number of distinct queues taken from. pub fn enlarge(amount: BalanceOf, max_bids: u32) -> (u32, u32) { - let total_issuance = - T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); let mut remaining = amount; let mut bids_taken = 0; let mut queues_hit = 0; let now = frame_system::Pallet::::block_number(); - Summary::::mutate(|totals| { - QueueTotals::::mutate(|qs| { - for duration in (1..=T::QueueCount::get()).rev() { - if qs[duration as usize - 1].0 == 0 { - continue - } - let queue_index = duration as usize - 1; - let expiry = - now.saturating_add(T::Period::get().saturating_mul(duration.into())); - Queues::::mutate(duration, |q| { - while let Some(mut bid) = q.pop() { - if remaining < bid.amount { - let overflow = bid.amount - remaining; - bid.amount = remaining; - q.try_push(Bid { amount: overflow, who: bid.who.clone() }) - .expect("just popped, so there must be space. qed"); - } - let amount = bid.amount; - - // Can never overflow due to block above. - remaining -= amount; - // Should never underflow since it should track the total of the - // bids exactly, but we'll be defensive. - qs[queue_index].1 = - qs[queue_index].1.defensive_saturating_sub(bid.amount); - - // Now to activate the bid... - let nonbond_issuance = - total_issuance.defensive_saturating_sub(totals.frozen); - let effective_issuance = totals - .proportion - .left_from_one() - .saturating_reciprocal_mul(nonbond_issuance); - let n = amount; - let d = effective_issuance; - let proportion = Perquintill::from_rational(n, d); - let who = bid.who; - let index = totals.index; - totals.frozen += bid.amount; - totals.proportion = - totals.proportion.defensive_saturating_add(proportion); - totals.index += 1; - let e = Event::Issued { index, expiry, who: who.clone(), amount }; - Self::deposit_event(e); - let bond = - ReceiptRecord { amount, proportion, who: who.clone(), expiry }; - Receipts::::insert(index, bond); - - // issue the fungible counterpart - let fung_eq = T::CounterpartAmount::convert(proportion); - let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); - - bids_taken += 1; - - if remaining.is_zero() || bids_taken == max_bids { - break - } - } - queues_hit += 1; - qs[queue_index].0 = q.len() as u32; - }); - if remaining.is_zero() || bids_taken == max_bids { - break - } + let mut summary = Summary::::get(); + let mut queue_totals = QueueTotals::::get(); + let our_account = Self::account_id(); + let issuance = Self::issuance_with(&our_account, &summary); + + for duration in (1..=T::QueueCount::get()).rev() { + if queue_totals[duration as usize - 1].0 == 0 { + continue + } + let queue_index = duration as usize - 1; + let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into())); + let mut queue = Queues::::get(&duration); + while let Some(mut bid) = queue.pop() { + if remaining < bid.amount { + let overflow = bid.amount - remaining; + bid.amount = remaining; + queue + .try_push(Bid { amount: overflow, who: bid.who.clone() }) + .expect("just popped, so there must be space. qed"); } - }); - }); + let amount = + bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); + if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { + continue + } + + // Can never overflow due to block above. + remaining.saturating_reduce(amount); + // Should never underflow since it should track the total of the + // bids exactly, but we'll be defensive. + queue_totals[queue_index].1 = + queue_totals[queue_index].1.defensive_saturating_sub(amount); + + // Now to activate the bid... + let n = amount; + let d = issuance.effective; + let proportion = Perquintill::from_rational(n, d); + let who = bid.who; + let index = summary.index; + summary.proportion_owed.defensive_saturating_accrue(proportion); + summary.index += 1; + + let e = Event::Issued { + index, + expiry, + who: who.clone(), + amount, + proportion: proportion.clone(), + }; + Self::deposit_event(e); + let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; + Receipts::::insert(index, receipt); + + // issue the fungible counterpart + let fung_eq = T::CounterpartAmount::convert(proportion); + let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); + + bids_taken += 1; + + if remaining.is_zero() || bids_taken == max_bids { + break + } + } + queues_hit += 1; + queue_totals[queue_index].0 = queue.len() as u32; + Queues::::insert(&duration, &queue); + if remaining.is_zero() || bids_taken == max_bids { + break + } + } + QueueTotals::::put(&queue_totals); + Summary::::put(&summary); (bids_taken, queues_hit) } } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index f1ebeb465eeca..9bf3fe0c66729 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -122,8 +122,8 @@ impl pallet_nis::Config for Test { type Currency = Balances; type CurrencyBalance = >::Balance; type AdminOrigin = frame_system::EnsureSignedBy; + type FundOrigin = frame_system::EnsureSigned; type Deficit = (); - type Surplus = (); type IgnoredIssuance = IgnoredIssuance; type Counterpart = NisBalances; type CounterpartAmount = WithMaximumOf>; diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 784de932b21e5..31b1a4d82bf33 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -42,8 +42,7 @@ fn basic_setup_works() { assert_eq!( Summary::::get(), SummaryRecord { - frozen: 0, - proportion: Perquintill::zero(), + proportion_owed: Perquintill::zero(), index: 0, target: Perquintill::zero(), } @@ -63,8 +62,7 @@ fn set_target_works() { assert_eq!( Summary::::get(), SummaryRecord { - frozen: 0, - proportion: Perquintill::zero(), + proportion_owed: Perquintill::zero(), index: 0, target: Perquintill::from_percent(50), } @@ -86,7 +84,7 @@ fn place_bid_works() { Error::::DurationTooBig ); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_eq!(pot(), 10); + assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); }); @@ -101,10 +99,10 @@ fn place_bid_queuing_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2)); assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2)); - assert_eq!(pot(), 45); + assert_eq!(Balances::reserved_balance(1), 45); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2)); - assert_eq!(pot(), 60); + assert_eq!(Balances::reserved_balance(1), 60); assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); assert_eq!( Queues::::get(2), @@ -140,7 +138,8 @@ fn multiple_place_bids_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_eq!(pot(), 50); + assert_eq!(Balances::reserved_balance(1), 40); + assert_eq!(Balances::reserved_balance(2), 10); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), @@ -163,7 +162,7 @@ fn retract_single_item_queue_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_eq!(pot(), 10); + assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(1), vec![]); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); @@ -180,7 +179,8 @@ fn retract_with_other_and_duplicate_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_eq!(pot(), 30); + assert_eq!(Balances::reserved_balance(1), 20); + assert_eq!(Balances::reserved_balance(2), 10); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); assert_eq!( Queues::::get(2), @@ -215,7 +215,10 @@ fn basic_enlarge_works() { Nis::enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount - assert_eq!(pot(), 80); + assert_eq!(Balances::reserved_balance(1), 40); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(pot(), 40); + assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(Queues::::get(2), vec![]); assert_eq!(QueueTotals::::get(), vec![(1, 40), (0, 0), (0, 0)]); @@ -223,20 +226,14 @@ fn basic_enlarge_works() { assert_eq!( Summary::::get(), SummaryRecord { - frozen: 40, - proportion: Perquintill::from_percent(10), + proportion_owed: Perquintill::from_percent(10), index: 1, target: Perquintill::zero(), } ); assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 2, - expiry: 7 - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } ); }); } @@ -259,27 +256,16 @@ fn enlarge_respects_bids_limit() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 4, - expiry: 10, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 4, expiry: 10 } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 2, - expiry: 7 - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } ); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 80, - proportion: Perquintill::from_percent(20), + proportion_owed: Perquintill::from_percent(20), index: 2, target: Perquintill::zero(), } @@ -300,18 +286,12 @@ fn enlarge_respects_amount_limit_and_will_split() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 1, - expiry: 4 - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 4 } ); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 40, - proportion: Perquintill::from_percent(10), + proportion_owed: Perquintill::from_percent(10), index: 1, target: Perquintill::zero(), } @@ -324,26 +304,69 @@ fn basic_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_eq!(Nis::issuance().effective, 400); + assert_eq!(Balances::free_balance(1), 60); + assert_eq!(Balances::reserved_balance(1), 40); + assert_eq!(pot(), 0); + Nis::enlarge(40, 1); + assert_eq!(Nis::issuance().effective, 400); + assert_eq!(Balances::free_balance(1), 60); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(pot(), 40); + run_to_block(3); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), Error::::NotExpired); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotExpired); run_to_block(4); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1), Error::::Unknown); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0), Error::::NotOwner); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Unknown); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), Error::::NotOwner); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_eq!(Nis::issuance().effective, 400); + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(pot(), 0); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 0, - proportion: Perquintill::zero(), + proportion_owed: Perquintill::zero(), index: 1, target: Perquintill::zero(), } ); assert_eq!(Receipts::::get(0), None); + }); +} + +#[test] +fn partial_thaw_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + Nis::enlarge(80, 1); + assert_eq!(pot(), 80); + + run_to_block(4); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1050000))); + + assert_eq!(Nis::issuance().effective, 400); + assert_eq!(Balances::free_balance(1), 40); + assert_eq!(pot(), 60); + + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + + assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 100); assert_eq!(pot(), 0); + + assert_eq!( + Summary::::get(), + SummaryRecord { + proportion_owed: Perquintill::zero(), + index: 1, + target: Perquintill::zero(), + } + ); + assert_eq!(Receipts::::get(0), None); }); } @@ -359,14 +382,14 @@ fn thaw_respects_transfers() { assert_ok!(Nis::transfer(&0, &2)); // Transfering the receipt... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), Error::::NotOwner); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotOwner); // ...can't be thawed due to missing counterpart - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), TokenError::NoFunds); // Transfer the counterpart also... assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 2100000)); // ...and thawing is possible. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0, None)); assert_eq!(Balances::free_balance(2), 140); assert_eq!(Balances::free_balance(1), 60); @@ -389,15 +412,20 @@ fn thaw_when_issuance_higher_works() { run_to_block(4); + // Unfunded initially... + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + // ...so we fund. + assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + // Transfer counterpart away... assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 250_000)); // ...and it's not thawable. - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), TokenError::NoFunds); // Transfer counterpart back... assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(2), 1, 250_000)); // ...and it is. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); @@ -420,7 +448,12 @@ fn thaw_with_ignored_issuance_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50)); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + // Unfunded initially... + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + // ...so we fund... + assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + // ...and then it's ok. + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); // Account zero changes have been ignored. assert_eq!(Balances::free_balance(1), 150); @@ -441,7 +474,7 @@ fn thaw_when_issuance_lower_works() { Balances::make_free_balance_be(&4, 75); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 75); assert_eq!(Balances::reserved_balance(1), 0); @@ -461,11 +494,12 @@ fn multiple_thaws_works() { Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); + assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -485,11 +519,12 @@ fn multiple_thaws_works_in_alternative_thaw_order() { Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); + assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -523,27 +558,16 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 2, - expiry: 13, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 13 } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 3, - expiry: 13, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 3, expiry: 13 } ); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 80, - proportion: Perquintill::from_percent(20), + proportion_owed: Perquintill::from_percent(20), index: 2, target: Perquintill::from_percent(40), } @@ -554,8 +578,7 @@ fn enlargement_to_target_works() { assert_eq!( Summary::::get(), SummaryRecord { - frozen: 80, - proportion: Perquintill::from_percent(20), + proportion_owed: Perquintill::from_percent(20), index: 2, target: Perquintill::from_percent(40), } @@ -565,27 +588,16 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Receipts::::get(2).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 1, - expiry: 12, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 12 } ); assert_eq!( Receipts::::get(3).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 2, - expiry: 12, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 12 } ); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 160, - proportion: Perquintill::from_percent(40), + proportion_owed: Perquintill::from_percent(40), index: 4, target: Perquintill::from_percent(40), } @@ -596,8 +608,7 @@ fn enlargement_to_target_works() { assert_eq!( Summary::::get(), SummaryRecord { - frozen: 160, - proportion: Perquintill::from_percent(40), + proportion_owed: Perquintill::from_percent(40), index: 4, target: Perquintill::from_percent(40), } @@ -610,19 +621,13 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Receipts::::get(4).unwrap(), - ReceiptRecord { - proportion: Perquintill::from_percent(10), - amount: 40, - who: 1, - expiry: 13, - } + ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 13 } ); assert_eq!( Summary::::get(), SummaryRecord { - frozen: 200, - proportion: Perquintill::from_percent(50), + proportion_owed: Perquintill::from_percent(50), index: 5, target: Perquintill::from_percent(60), } diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index ea51f32a81a2a..fa7caa26ab7be 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -49,6 +49,7 @@ pub trait WeightInfo { fn retract_bid(l: u32, ) -> Weight; fn set_target() -> Weight; fn thaw() -> Weight; + fn fund_deficit() -> Weight; fn pursue_target_noop() -> Weight; fn pursue_target_per_item(b: u32, ) -> Weight; fn pursue_target_per_queue(q: u32, ) -> Weight; @@ -95,6 +96,13 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + // Storage: Nis Active (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + fn fund_deficit() -> Weight { + Weight::from_ref_time(47_753_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } // Storage: Nis ActiveTotal (r:1 w:0) fn pursue_target_noop() -> Weight { Weight::from_ref_time(1_663_000 as u64) @@ -167,6 +175,13 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } + // Storage: Nis Active (r:1 w:1) + // Storage: Nis ActiveTotal (r:1 w:1) + fn fund_deficit() -> Weight { + Weight::from_ref_time(47_753_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } // Storage: Nis ActiveTotal (r:1 w:0) fn pursue_target_noop() -> Weight { Weight::from_ref_time(1_663_000 as u64) diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index 1297956b2f64e..c7d60234c1a6a 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -21,7 +21,7 @@ use crate::dispatch::Parameter; use codec::{CompactLen, Decode, DecodeLimit, Encode, EncodeLike, Input, MaxEncodedLen}; use impl_trait_for_tuples::impl_for_tuples; use scale_info::{build::Fields, meta_type, Path, Type, TypeInfo, TypeParameter}; -use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, Saturating}; +use sp_arithmetic::traits::{CheckedAdd, CheckedMul, CheckedSub, One, Saturating}; use sp_core::bounded::bounded_vec::TruncateFrom; #[doc(hidden)] pub use sp_runtime::traits::{ @@ -348,17 +348,25 @@ impl DefensiveOption for Option { /// A variant of [`Defensive`] with the same rationale, for the arithmetic operations where in /// case an infallible operation fails, it saturates. pub trait DefensiveSaturating { - /// Add `self` and `other` defensively. + /// Return `self` plus `other` defensively. fn defensive_saturating_add(self, other: Self) -> Self; - /// Subtract `other` from `self` defensively. + /// Return `self` minus `other` defensively. fn defensive_saturating_sub(self, other: Self) -> Self; - /// Multiply `self` and `other` defensively. + /// Return the product of `self` and `other` defensively. fn defensive_saturating_mul(self, other: Self) -> Self; + /// Increase `self` by `other` defensively. + fn defensive_saturating_accrue(&mut self, other: Self); + /// Reduce `self` by `other` defensively. + fn defensive_saturating_reduce(&mut self, other: Self); + /// Increment `self` by one defensively. + fn defensive_saturating_inc(&mut self); + /// Decrement `self` by one defensively. + fn defensive_saturating_dec(&mut self); } // NOTE: A bit unfortunate, since T has to be bound by all the traits needed. Could make it // `DefensiveSaturating` to mitigate. -impl DefensiveSaturating for T { +impl DefensiveSaturating for T { fn defensive_saturating_add(self, other: Self) -> Self { self.checked_add(&other).defensive_unwrap_or_else(|| self.saturating_add(other)) } @@ -368,6 +376,18 @@ impl DefensiveSaturating f fn defensive_saturating_mul(self, other: Self) -> Self { self.checked_mul(&other).defensive_unwrap_or_else(|| self.saturating_mul(other)) } + fn defensive_saturating_accrue(&mut self, other: Self) { + *self = sp_std::mem::replace(self, One::one()).defensive_saturating_add(other); + } + fn defensive_saturating_reduce(&mut self, other: Self) { + *self = sp_std::mem::replace(self, One::one()).defensive_saturating_sub(other); + } + fn defensive_saturating_inc(&mut self) { + self.defensive_saturating_accrue(One::one()); + } + fn defensive_saturating_dec(&mut self) { + self.defensive_saturating_reduce(One::one()); + } } /// Construct an object by defensively truncating an input if the `TryFrom` conversion fails. diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index 244242c0f7580..d7b326164b7d3 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -42,8 +42,8 @@ pub mod traits; pub use fixed_point::{FixedI128, FixedI64, FixedPointNumber, FixedPointOperand, FixedU128}; pub use per_things::{ - InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, Rounding, SignedRounding, - UpperOf, + InnerOf, MultiplyArg, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, RationalArg, + ReciprocalArg, Rounding, SignedRounding, UpperOf, }; pub use rational::{Rational128, RationalInfinite}; diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index 2932a742063db..fc3767761175c 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -36,6 +36,57 @@ pub type InnerOf

=

::Inner; /// Get the upper type of a `PerThing`. pub type UpperOf

=

::Upper; +pub trait RationalArg: + Clone + + Ord + + ops::Div + + ops::Rem + + ops::Add + + ops::AddAssign + + Unsigned + + Zero + + One +{ +} + +impl< + T: Clone + + Ord + + ops::Div + + ops::Rem + + ops::Add + + ops::AddAssign + + Unsigned + + Zero + + One, + > RationalArg for T +{ +} + +pub trait MultiplyArg: + Clone + + ops::Rem + + ops::Div + + ops::Mul + + ops::Add + + Unsigned +{ +} + +impl< + T: Clone + + ops::Rem + + ops::Div + + ops::Mul + + ops::Add + + Unsigned, + > MultiplyArg for T +{ +} + +pub trait ReciprocalArg: MultiplyArg + Saturating {} +impl ReciprocalArg for T {} + /// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per /// `X`_. pub trait PerThing: @@ -160,13 +211,7 @@ pub trait PerThing: /// ``` fn mul_floor(self, b: N) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Rem - + ops::Div - + ops::Mul - + ops::Add - + Unsigned, + N: MultiplyArg + UniqueSaturatedInto, Self::Inner: Into, { overflow_prune_mul::(b, self.deconstruct(), Rounding::Down) @@ -189,13 +234,7 @@ pub trait PerThing: /// ``` fn mul_ceil(self, b: N) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Rem - + ops::Div - + ops::Mul - + ops::Add - + Unsigned, + N: MultiplyArg + UniqueSaturatedInto, Self::Inner: Into, { overflow_prune_mul::(b, self.deconstruct(), Rounding::Up) @@ -212,14 +251,7 @@ pub trait PerThing: /// ``` fn saturating_reciprocal_mul(self, b: N) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Rem - + ops::Div - + ops::Mul - + ops::Add - + Saturating - + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto, Self::Inner: Into, { saturating_reciprocal_mul::(b, self.deconstruct(), Rounding::NearestPrefUp) @@ -239,14 +271,7 @@ pub trait PerThing: /// ``` fn saturating_reciprocal_mul_floor(self, b: N) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Rem - + ops::Div - + ops::Mul - + ops::Add - + Saturating - + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto, Self::Inner: Into, { saturating_reciprocal_mul::(b, self.deconstruct(), Rounding::Down) @@ -266,14 +291,7 @@ pub trait PerThing: /// ``` fn saturating_reciprocal_mul_ceil(self, b: N) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Rem - + ops::Div - + ops::Mul - + ops::Add - + Saturating - + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto, Self::Inner: Into, { saturating_reciprocal_mul::(b, self.deconstruct(), Rounding::Up) @@ -316,17 +334,7 @@ pub trait PerThing: /// ``` fn from_rational(p: N, q: N) -> Self where - N: Clone - + Ord - + TryInto - + TryInto - + ops::Div - + ops::Rem - + ops::Add - + ops::AddAssign - + Unsigned - + Zero - + One, + N: RationalArg + TryInto + TryInto, Self::Inner: Into, { Self::from_rational_with_rounding(p, q, Rounding::Down).unwrap_or_else(|_| Self::one()) @@ -388,34 +396,14 @@ pub trait PerThing: /// ``` fn from_rational_with_rounding(p: N, q: N, rounding: Rounding) -> Result where - N: Clone - + Ord - + TryInto - + TryInto - + ops::Div - + ops::Rem - + ops::Add - + ops::AddAssign - + Unsigned - + Zero - + One, + N: RationalArg + TryInto + TryInto, Self::Inner: Into; /// Same as `Self::from_rational`. #[deprecated = "Use from_rational instead"] fn from_rational_approximation(p: N, q: N) -> Self where - N: Clone - + Ord - + TryInto - + TryInto - + ops::Div - + ops::Rem - + ops::Add - + ops::AddAssign - + Unsigned - + Zero - + One, + N: RationalArg + TryInto + TryInto, Self::Inner: Into, { Self::from_rational(p, q) @@ -495,13 +483,7 @@ where /// Overflow-prune multiplication. Accurately multiply a value by `self` without overflowing. fn overflow_prune_mul(x: N, part: P::Inner, rounding: Rounding) -> N where - N: Clone - + UniqueSaturatedInto - + ops::Div - + ops::Mul - + ops::Add - + ops::Rem - + Unsigned, + N: MultiplyArg + UniqueSaturatedInto, P: PerThing, P::Inner: Into, { @@ -517,12 +499,7 @@ where /// to `x / denom * numer` for an accurate result. fn rational_mul_correction(x: N, numer: P::Inner, denom: P::Inner, rounding: Rounding) -> N where - N: UniqueSaturatedInto - + ops::Div - + ops::Mul - + ops::Add - + ops::Rem - + Unsigned, + N: MultiplyArg + UniqueSaturatedInto, P: PerThing, P::Inner: Into, { @@ -803,17 +780,7 @@ macro_rules! implement_per_thing { #[deprecated = "Use `PerThing::from_rational` instead"] pub fn from_rational_approximation(p: N, q: N) -> Self where - N: Clone - + Ord - + TryInto<$type> - + TryInto<$upper_type> - + ops::Div - + ops::Rem - + ops::Add - + ops::AddAssign - + Unsigned - + Zero - + One, + N: RationalArg+ TryInto<$type> + TryInto<$upper_type>, $type: Into { ::from_rational(p, q) @@ -822,17 +789,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::from_rational`]. pub fn from_rational(p: N, q: N) -> Self where - N: Clone - + Ord - + TryInto<$type> - + TryInto<$upper_type> - + ops::Div - + ops::Rem - + ops::Add - + ops::AddAssign - + Unsigned - + Zero - + One, + N: RationalArg+ TryInto<$type> + TryInto<$upper_type>, $type: Into { ::from_rational(p, q) @@ -851,9 +808,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::mul_floor`]. pub fn mul_floor(self, b: N) -> N where - N: Clone + UniqueSaturatedInto<$type> + - ops::Rem + ops::Div + ops::Mul + - ops::Add + Unsigned, + N: MultiplyArg + UniqueSaturatedInto<$type>, $type: Into, { @@ -863,9 +818,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::mul_ceil`]. pub fn mul_ceil(self, b: N) -> N where - N: Clone + UniqueSaturatedInto<$type> + - ops::Rem + ops::Div + ops::Mul + - ops::Add + Unsigned, + N: MultiplyArg + UniqueSaturatedInto<$type>, $type: Into, { PerThing::mul_ceil(self, b) @@ -874,9 +827,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::saturating_reciprocal_mul`]. pub fn saturating_reciprocal_mul(self, b: N) -> N where - N: Clone + UniqueSaturatedInto<$type> + ops::Rem + - ops::Div + ops::Mul + ops::Add + - Saturating + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto<$type>, $type: Into, { PerThing::saturating_reciprocal_mul(self, b) @@ -885,9 +836,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::saturating_reciprocal_mul_floor`]. pub fn saturating_reciprocal_mul_floor(self, b: N) -> N where - N: Clone + UniqueSaturatedInto<$type> + ops::Rem + - ops::Div + ops::Mul + ops::Add + - Saturating + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto<$type>, $type: Into, { PerThing::saturating_reciprocal_mul_floor(self, b) @@ -896,9 +845,7 @@ macro_rules! implement_per_thing { /// See [`PerThing::saturating_reciprocal_mul_ceil`]. pub fn saturating_reciprocal_mul_ceil(self, b: N) -> N where - N: Clone + UniqueSaturatedInto<$type> + ops::Rem + - ops::Div + ops::Mul + ops::Add + - Saturating + Unsigned, + N: ReciprocalArg + UniqueSaturatedInto<$type>, $type: Into, { PerThing::saturating_reciprocal_mul_ceil(self, b) @@ -1133,6 +1080,11 @@ macro_rules! implement_per_thing { } } + impl $crate::traits::One for $name { + fn one() -> Self { + Self::one() + } + } #[cfg(test)] mod $test_mod { diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 1c48b1933431d..452cb44736a65 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -515,6 +515,11 @@ impl Convert for Identity { a } } +impl ConvertBack for Identity { + fn convert_back(a: T) -> T { + a + } +} /// A structure that performs standard conversion using the standard Rust conversion traits. pub struct ConvertInto; @@ -524,6 +529,12 @@ impl> Convert for ConvertInto { } } +/// Extensible conversion trait. Generic over both source and destination types. +pub trait ConvertBack: Convert { + /// Make conversion back. + fn convert_back(a: B) -> A; +} + /// Convenience type to work around the highly unergonomic syntax needed /// to invoke the functions of overloaded generic traits, in this case /// `TryFrom` and `TryInto`. From b232a35fdfbc3dc3de53e4f95a412d8e606b9c47 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 14:40:39 +0000 Subject: [PATCH 09/48] Docs --- frame/nis/src/lib.rs | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 8a3a9996363f9..fbeeeb11a1dc7 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -26,7 +26,7 @@ //! //! ## Design //! -//! Queues for each of 1-`QueueCount` periods, given in blocks (`Period`). Queues are limited in +//! Queues for each of `1..QueueCount` periods, given in blocks (`Period`). Queues are limited in //! size to something sensible, `MaxQueueLen`. A secondary storage item with `QueueCount` x `u32` //! elements with the number of items in each queue. //! @@ -40,28 +40,34 @@ //! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of //! `QueueCount`. The balance gets reserved. There's a minimum of `MinFreeze` to avoid dust. //! -//! Until your bid is turned into an issued bond you can retract it instantly and the funds are -//! unreserved. +//! Until your bid is consolidated and you receive a receipt, you can retract it instantly and the +//! funds are unreserved. //! -//! There's a target proportion of effective total issuance (i.e. accounting for existing bonds) -//! which the we attempt to have frozen at any one time. It will likely be gradually increased over -//! time by governance. +//! There's a target proportion of effective total issuance (i.e. accounting for existing receipts) +//! which the pallet attempts to have frozen at any one time. It will likely be gradually increased +//! over time by governance. //! -//! As the total funds frozen under bonds drops below `FrozenFraction` of the total effective -//! issuance, then bids are taken from queues, with the queue of the greatest period taking -//! priority. If the item in the queue's locked amount is greater than the amount left to be -//! frozen, then it is split up into multiple bids and becomes partially frozen under bond. +//! As the proportion of effective total issuance represented by outstanding receipts drops below +//! `FrozenFraction`, then bids are taken from queues and consolidated into receipts, with the queue +//! of the greatest period taking priority. If the item in the queue's locked amount is greater than +//! the amount remaining to achieve `FrozenFraction`, then it is split up into multiple bids and +//! becomes partially consolidated. //! -//! Once an account's balance is frozen, it remains frozen until the owner thaws the balance of the -//! account. This may happen no earlier than queue's period after the point at which the bond is -//! issued. +//! With the consolidation of a bid, the bid amount is taken from the owner and a receipt is issued. +//! The receipt records the proportion of the bid compared to effective total issuance at the time +//! of consolidation. The receipt has two independent elements: a "main" non-fungible receipt and +//! a second set of fungible "counterpart" tokens. The accounting functionality of the latter must +//! be provided through the `Counterpart` trait item. The main non-fungible receipt may have its +//! owner transferred through the pallet's implementation of `nonfungible::Transfer`. //! -//! ## Suggested Values +//! A later `thaw` function may be called in order to reduce the recorded proportion or entirely +//! remove the receipt in return for the appropriate proportion of the effective total issuance. +//! This may happen no earlier than queue's period after the point at which the receipt was issued. +//! The call must be made by the owner of both the "main" non-fungible receipt and the appropriate +//! amount of counterpart tokens. //! -//! - `QueueCount`: 300 -//! - `Period`: 432,000 -//! - `MaxQueueLen`: 1000 -//! - `MinFreeze`: Around CHF 100 in value. +//! `NoCounterpart` may be provided as an implementation for the counterpart token system in which +//! case they are completely disregarded from the thawing logic. #![cfg_attr(not(feature = "std"), no_std)] @@ -101,6 +107,8 @@ where } } +//TODO: impl `ExchangeAsset`. + #[frame_support::pallet] pub mod pallet { pub use crate::weights::WeightInfo; From 66fa13b2aa3d5b0ecc08dffa88051f1754347320 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 15:37:42 +0000 Subject: [PATCH 10/48] Remove AdminOrigin --- frame/nis/src/lib.rs | 63 +++++++++++------------------ frame/nis/src/mock.rs | 7 +++- frame/nis/src/tests.rs | 90 ++++++------------------------------------ 3 files changed, 40 insertions(+), 120 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index fbeeeb11a1dc7..e68f04c491010 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -68,6 +68,11 @@ //! //! `NoCounterpart` may be provided as an implementation for the counterpart token system in which //! case they are completely disregarded from the thawing logic. +//! +//! ## Terms +//! +//! - *Effective total issuance*: The total issuance of balances in the system, including all claims +//! of all outstanding receipts but excluding `IgnoredIssuance`. #![cfg_attr(not(feature = "std"), no_std)] @@ -207,14 +212,11 @@ pub mod pallet { + TypeInfo + MaxEncodedLen; - /// Origin required for setting the target proportion to be under bond. - type AdminOrigin: EnsureOrigin; - /// Origin required for auto-funding the deficit. type FundOrigin: EnsureOrigin; /// The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get - /// the issuance with which we determine the thawed value of a bond. + /// the issuance with which we determine the thawed value of a given proportion. type IgnoredIssuance: Get>; type Counterpart: FungibleMutate; @@ -228,6 +230,9 @@ pub mod pallet { /// freezing period). type Deficit: OnUnbalanced>; + /// The target sum of all receipts' proportions. + type Target: Get; + /// Number of duration queues in total. This sets the maximum duration supported, which is /// this value multiplied by `Period`. #[pallet::constant] @@ -248,26 +253,25 @@ pub mod pallet { #[pallet::constant] type Period: Get; - /// The minimum amount of funds that may be offered to freeze for a bond. Note that this - /// does not actually limit the amount which may be frozen in a bond since bonds may be - /// split up in order to satisfy the desired amount of funds under bonds. + /// The minimum amount of funds that may be placed in a bid. Note that this + /// does not actually limit the amount which may be represented in a receipt since bids may + /// be split up by the system. /// /// It should be at least big enough to ensure that there is no possible storage spam attack /// or queue-filling attack. #[pallet::constant] type MinFreeze: Get>; - /// The number of blocks between consecutive attempts to issue more bonds in an effort to - /// get to the target amount to be frozen. + /// The number of blocks between consecutive attempts to dequeue bids and create receipts. /// /// A larger value results in fewer storage hits each block, but a slower period to get to /// the target. #[pallet::constant] type IntakePeriod: Get; - /// The maximum amount of bids that can become bonds each block. A larger - /// value here means less of the block available for transactions should there be a glut of - /// bids to make into bonds to reach the target. + /// The maximum amount of bids that can consolidated into receipts in a single intake. A + /// larger value here means less of the block available for transactions should there be a + /// glut of bids. #[pallet::constant] type MaxIntakeBids: Get; @@ -280,7 +284,7 @@ pub mod pallet { #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(_); - /// A single bid on a bond, an item of a *queue* in `Queues`. + /// A single bid, an item of a *queue* in `Queues`. #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] @@ -291,13 +295,12 @@ pub mod pallet { pub who: AccountId, } - /// Information representing an active bond. + /// Information representing a receipt. #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] pub struct ReceiptRecord { - /// The proportion of the effective total issuance (i.e. accounting for any eventual bond - /// expansion or contraction that may eventually be claimed). + /// The proportion of the effective total issuance. pub proportion: Perquintill, /// The account to whom this bond belongs. pub who: AccountId, @@ -324,8 +327,6 @@ pub mod pallet { pub proportion_owed: Perquintill, /// The total number of bonds issued so far. pub index: ActiveIndex, - /// The target proportion of bonds within total issuance. - pub target: Perquintill, } /// The totals of items and balances within each queue. Saves a lot of storage reads in the @@ -547,22 +548,6 @@ pub mod pallet { Ok(().into()) } - /// Set target proportion of bonded-funds. - /// - /// Origin must be `AdminOrigin`. - /// - /// - `target`: The target proportion of effective issued funds that should be in bonds - /// at any one time. - #[pallet::weight(T::WeightInfo::set_target())] - pub fn set_target( - origin: OriginFor, - #[pallet::compact] target: Perquintill, - ) -> DispatchResultWithPostInfo { - T::AdminOrigin::ensure_origin(origin)?; - Summary::::mutate(|totals| totals.target = target); - Ok(().into()) - } - /// Ensure we have sufficient funding for all potential payouts. /// /// - `origin`: Must be accepted by `FundOrigin`. @@ -680,11 +665,6 @@ pub mod pallet { } impl Pallet { - /// Get the target amount of bonds that we're aiming for. - pub fn target() -> Perquintill { - Summary::::get().target - } - /// The account ID of the reserves. /// /// This actually does computation. If you need to keep using it, then make sure you cache @@ -721,8 +701,9 @@ pub mod pallet { /// of funds frozen into bonds. pub fn pursue_target(max_bids: u32) -> Weight { let summary: SummaryRecord = Summary::::get(); - if summary.proportion_owed < summary.target { - let missing = summary.target.saturating_sub(summary.proportion_owed); + let target = T::Target::get(); + if summary.proportion_owed < target { + let missing = target.saturating_sub(summary.proportion_owed); let issuance = Self::issuance_with(&Self::account_id(), &summary); let intake = missing * issuance.effective; diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 9bf3fe0c66729..ce4e3840aaa27 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -17,7 +17,7 @@ //! Test environment for NIS pallet. -use crate::{self as pallet_nis, WithMaximumOf}; +use crate::{self as pallet_nis, Perquintill, WithMaximumOf}; use frame_support::{ ord_parameter_types, parameter_types, @@ -110,23 +110,26 @@ impl pallet_balances::Config for Test { parameter_types! { pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored. pub const NisPalletId: PalletId = PalletId(*b"py/nis "); + pub static Target: Perquintill = Perquintill::zero(); } ord_parameter_types! { pub const One: u64 = 1; } +// TODO: Throttled thawing. + impl pallet_nis::Config for Test { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = >::Balance; - type AdminOrigin = frame_system::EnsureSignedBy; type FundOrigin = frame_system::EnsureSigned; type Deficit = (); type IgnoredIssuance = IgnoredIssuance; type Counterpart = NisBalances; type CounterpartAmount = WithMaximumOf>; + type Target = Target; type QueueCount = ConstU32<3>; type MaxQueueLen = ConstU32<3>; type FifoQueueLen = ConstU32<1>; diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 31b1a4d82bf33..2391307b2e935 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -21,7 +21,6 @@ use super::*; use crate::{mock::*, Error}; use frame_support::{ assert_noop, assert_ok, - dispatch::DispatchError, traits::{ nonfungible::{Inspect, Transfer}, Currency, @@ -41,35 +40,12 @@ fn basic_setup_works() { } assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::zero(), - index: 0, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::zero(), index: 0 } ); assert_eq!(QueueTotals::::get(), vec![(0, 0); 3]); }); } -#[test] -fn set_target_works() { - new_test_ext().execute_with(|| { - run_to_block(1); - let e = DispatchError::BadOrigin; - assert_noop!(Nis::set_target(RuntimeOrigin::signed(2), Perquintill::from_percent(50)), e); - assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(50))); - - assert_eq!( - Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::zero(), - index: 0, - target: Perquintill::from_percent(50), - } - ); - }); -} - #[test] fn place_bid_works() { new_test_ext().execute_with(|| { @@ -225,11 +201,7 @@ fn basic_enlarge_works() { assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(10), - index: 1, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(10), index: 1 } ); assert_eq!( Receipts::::get(0).unwrap(), @@ -264,11 +236,7 @@ fn enlarge_respects_bids_limit() { ); assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(20), - index: 2, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } ); }); } @@ -290,11 +258,7 @@ fn enlarge_respects_amount_limit_and_will_split() { ); assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(10), - index: 1, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(10), index: 1 } ); }); } @@ -327,11 +291,7 @@ fn basic_thaw_works() { assert_eq!(pot(), 0); assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::zero(), - index: 1, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::zero(), index: 1 } ); assert_eq!(Receipts::::get(0), None); }); @@ -360,11 +320,7 @@ fn partial_thaw_works() { assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::zero(), - index: 1, - target: Perquintill::zero(), - } + SummaryRecord { proportion_owed: Perquintill::zero(), index: 1 } ); assert_eq!(Receipts::::get(0), None); }); @@ -540,7 +496,7 @@ fn enlargement_to_target_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 3)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 3)); - assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(40))); + Target::set(Perquintill::from_percent(40)); run_to_block(3); assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 },]); @@ -566,22 +522,14 @@ fn enlargement_to_target_works() { ); assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(20), - index: 2, - target: Perquintill::from_percent(40), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } ); run_to_block(5); // No change assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(20), - index: 2, - target: Perquintill::from_percent(40), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } ); run_to_block(6); @@ -596,26 +544,18 @@ fn enlargement_to_target_works() { ); assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(40), - index: 4, - target: Perquintill::from_percent(40), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(40), index: 4 } ); run_to_block(8); // No change now. assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(40), - index: 4, - target: Perquintill::from_percent(40), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(40), index: 4 } ); // Set target a bit higher to use up the remaining bid. - assert_ok!(Nis::set_target(RuntimeOrigin::signed(1), Perquintill::from_percent(60))); + Target::set(Perquintill::from_percent(60)); run_to_block(10); // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. @@ -626,11 +566,7 @@ fn enlargement_to_target_works() { assert_eq!( Summary::::get(), - SummaryRecord { - proportion_owed: Perquintill::from_percent(50), - index: 5, - target: Perquintill::from_percent(60), - } + SummaryRecord { proportion_owed: Perquintill::from_percent(50), index: 5 } ); }); } From 3855ed0e8b07b9d44cd10060d85e36043f8fe1da Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 15:59:50 +0000 Subject: [PATCH 11/48] Integrate into Kitchen Sink --- bin/node/runtime/src/lib.rs | 19 +++++++++++++------ frame/nis/Cargo.toml | 3 ++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index c0e36c6377f71..2b9113db16d75 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -32,9 +32,10 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, + fungible::ItemOf, AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, Currency, + EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, + KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, + WithdrawReasons, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -53,6 +54,7 @@ use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; +use pallet_nis::WithMaximumOf; use pallet_session::historical::{self as pallet_session_historical}; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; @@ -1452,16 +1454,22 @@ parameter_types! { pub const MinFreeze: Balance = 100 * DOLLARS; pub const IntakePeriod: BlockNumber = 10; pub const MaxIntakeBids: u32 = 10; + pub Target: Perquintill = Perquintill::zero(); + pub const NisPalletId: PalletId = PalletId(*b"py/nis "); } impl pallet_nis::Config for Runtime { + type WeightInfo = pallet_nis::weights::SubstrateWeight; type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = Balance; - type AdminOrigin = frame_system::EnsureRoot; + type FundOrigin = frame_system::EnsureSigned; + type Counterpart = ItemOf, AccountId>; + type CounterpartAmount = WithMaximumOf>; type Deficit = (); - type Surplus = (); type IgnoredIssuance = IgnoredIssuance; + type Target = Target; + type PalletId = NisPalletId; type QueueCount = QueueCount; type MaxQueueLen = MaxQueueLen; type FifoQueueLen = FifoQueueLen; @@ -1469,7 +1477,6 @@ impl pallet_nis::Config for Runtime { type MinFreeze = MinFreeze; type IntakePeriod = IntakePeriod; type MaxIntakeBids = MaxIntakeBids; - type WeightInfo = pallet_nis::weights::SubstrateWeight; } parameter_types! { diff --git a/frame/nis/Cargo.toml b/frame/nis/Cargo.toml index 63872a58f0ef0..dd91deb28d334 100644 --- a/frame/nis/Cargo.toml +++ b/frame/nis/Cargo.toml @@ -19,12 +19,12 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } sp-arithmetic = { version = "5.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } -sp-core = { version = "6.0.0", path = "../../primitives/core" } sp-io = { version = "6.0.0", path = "../../primitives/io" } [features] @@ -36,6 +36,7 @@ std = [ "frame-system/std", "scale-info/std", "sp-arithmetic/std", + "sp-core/std", "sp-runtime/std", "sp-std/std", ] From ed21e766f669c981806cb6ba6a3a60a8ae680ec4 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 16:57:01 +0000 Subject: [PATCH 12/48] Thaw throttling --- bin/node/runtime/src/lib.rs | 8 ++- frame/nis/src/lib.rs | 62 +++++++++++++++------ frame/nis/src/mock.rs | 10 ++-- frame/nis/src/tests.rs | 106 ++++++++++++++++++++++++++++++------ 4 files changed, 147 insertions(+), 39 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 2b9113db16d75..ecda55720e68d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1451,9 +1451,11 @@ parameter_types! { pub const MaxQueueLen: u32 = 1000; pub const FifoQueueLen: u32 = 500; pub const Period: BlockNumber = 30 * DAYS; - pub const MinFreeze: Balance = 100 * DOLLARS; + pub const MinBid: Balance = 100 * DOLLARS; + pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const IntakePeriod: BlockNumber = 10; pub const MaxIntakeBids: u32 = 10; + pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); } @@ -1474,9 +1476,11 @@ impl pallet_nis::Config for Runtime { type MaxQueueLen = MaxQueueLen; type FifoQueueLen = FifoQueueLen; type Period = Period; - type MinFreeze = MinFreeze; + type MinBid = MinBid; + type MinReceipt = MinReceipt; type IntakePeriod = IntakePeriod; type MaxIntakeBids = MaxIntakeBids; + type ThawThrottle = ThawThrottle; } parameter_types! { diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index e68f04c491010..9dbb116fab1c8 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -38,7 +38,7 @@ //! bid size so that smaller bids fall off as it gets too large. //! //! Account may enqueue a balance with some number of `Period`s lock up, up to a maximum of -//! `QueueCount`. The balance gets reserved. There's a minimum of `MinFreeze` to avoid dust. +//! `QueueCount`. The balance gets reserved. There's a minimum of `MinBid` to avoid dust. //! //! Until your bid is consolidated and you receive a receipt, you can retract it instantly and the //! funds are unreserved. @@ -145,6 +145,7 @@ pub mod pallet { ::AccountId, ::BlockNumber, >; + type SummaryRecordOf = SummaryRecord<::BlockNumber>; pub struct NoCounterpart(sp_std::marker::PhantomData); impl FungibleInspect for NoCounterpart { @@ -197,6 +198,10 @@ pub mod pallet { /// Overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The treasury's pallet id, used for deriving its sovereign account ID. + #[pallet::constant] + type PalletId: Get; + /// Currency type that this works on. type Currency: ReservableCurrency; @@ -260,7 +265,12 @@ pub mod pallet { /// It should be at least big enough to ensure that there is no possible storage spam attack /// or queue-filling attack. #[pallet::constant] - type MinFreeze: Get>; + type MinBid: Get>; + + /// The minimum amount of funds which may intentionally be left remaining under a single + /// receipt. + #[pallet::constant] + type MinReceipt: Get; /// The number of blocks between consecutive attempts to dequeue bids and create receipts. /// @@ -275,9 +285,9 @@ pub mod pallet { #[pallet::constant] type MaxIntakeBids: Get; - /// The treasury's pallet id, used for deriving its sovereign account ID. + /// The maximum proportion which may be thawed and the period over which it is reset. #[pallet::constant] - type PalletId: Get; + type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>; } #[pallet::pallet] @@ -322,11 +332,15 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct SummaryRecord { + pub struct SummaryRecord { /// The proportion of funds that the `frozen` balance represents to total issuance. pub proportion_owed: Perquintill, /// The total number of bonds issued so far. pub index: ActiveIndex, + /// The amount (as a proportion of ETI) which has been thawed in this period so far. + pub thawed: Perquintill, + /// The current thaw period's beginning. + pub last_period: BlockNumber, } /// The totals of items and balances within each queue. Saves a lot of storage reads in the @@ -350,7 +364,7 @@ pub mod pallet { /// Information relating to the bonds currently active. #[pallet::storage] - pub type Summary = StorageValue<_, SummaryRecord, ValueQuery>; + pub type Summary = StorageValue<_, SummaryRecordOf, ValueQuery>; /// The currently active bonds, indexed according to the order of creation. #[pallet::storage] @@ -432,6 +446,10 @@ pub mod pallet { Unfunded, /// There are enough funds for what is required. Funded, + /// The thaw throttle has been reached for this period. + Throttled, + /// The operation would result in a receipt worth an insignficant value. + MakesDust, } #[pallet::hooks] @@ -453,7 +471,7 @@ pub mod pallet { /// /// - `amount`: The amount of the bid; these funds will be reserved. If the bid is /// successfully elevated into a bond, then these funds will continue to be - /// reserved until the bond thaws. Must be at least `MinFreeze`. + /// reserved until the bond thaws. Must be at least `MinBid`. /// - `duration`: The number of periods before which the bond may be thawed. /// Must be greater than 1 and no more than `QueueCount`. /// @@ -467,7 +485,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; - ensure!(amount >= T::MinFreeze::get(), Error::::AmountTooSmall); + ensure!(amount >= T::MinBid::get(), Error::::AmountTooSmall); let queue_count = T::QueueCount::get() as usize; let queue_index = duration.checked_sub(1).ok_or(Error::::DurationTooSmall)? as usize; ensure!(queue_index < queue_count, Error::::DurationTooBig); @@ -527,7 +545,6 @@ pub mod pallet { let queue_count = T::QueueCount::get() as usize; let queue_index = duration.checked_sub(1).ok_or(Error::::DurationTooSmall)? as usize; ensure!(queue_index < queue_count, Error::::DurationTooBig); - // TODO: ensure!(have_in_reserve(amount)) let bid = Bid { amount, who }; let new_len = Queues::::try_mutate(duration, |q| -> Result { @@ -554,7 +571,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::fund_deficit())] pub fn fund_deficit(origin: OriginFor) -> DispatchResult { T::FundOrigin::ensure_origin(origin)?; - let summary: SummaryRecord = Summary::::get(); + let summary: SummaryRecordOf = Summary::::get(); let our_account = Self::account_id(); let issuance = Self::issuance_with(&our_account, &summary); let deficit = issuance.required.saturating_sub(issuance.holdings); @@ -586,19 +603,32 @@ pub mod pallet { let now = frame_system::Pallet::::block_number(); ensure!(now >= receipt.expiry, Error::::NotExpired); + let mut summary: SummaryRecordOf = Summary::::get(); + let proportion = if let Some(counterpart) = portion { let proportion = T::CounterpartAmount::convert_back(counterpart); ensure!(proportion <= receipt.proportion, Error::::TooMuch); - // TODO: check if `proportion` is below a minimum bond size and if so then fail. + let remaining = receipt.proportion.saturating_sub(proportion); + ensure!( + remaining.is_zero() || remaining >= T::MinReceipt::get(), + Error::::MakesDust + ); proportion } else { - let fung_eq = T::CounterpartAmount::convert(receipt.proportion); - T::Counterpart::burn_from(&who, fung_eq)?; receipt.proportion }; + let (throttle, throttle_period) = T::ThawThrottle::get(); + if now.saturating_sub(summary.last_period) >= throttle_period { + summary.thawed = Zero::zero(); + summary.last_period = now; + } + summary.thawed.saturating_accrue(proportion); + ensure!(summary.thawed <= throttle, Error::::Throttled); + + T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(proportion))?; + // Multiply the proportion it is by the total issued. - let mut summary: SummaryRecord = Summary::::get(); let our_account = Self::account_id(); let effective_issuance = Self::issuance_with(&our_account, &summary).effective; let amount = proportion * effective_issuance; @@ -685,7 +715,7 @@ pub mod pallet { /// Also provides the summary and this pallet's account ID. pub fn issuance_with( our_account: &T::AccountId, - summary: &SummaryRecord, + summary: &SummaryRecordOf, ) -> IssuanceInfo> { let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); @@ -700,7 +730,7 @@ pub mod pallet { /// Attempt to enlarge our bond-set from bids in order to satisfy our desired target amount /// of funds frozen into bonds. pub fn pursue_target(max_bids: u32) -> Weight { - let summary: SummaryRecord = Summary::::get(); + let summary: SummaryRecordOf = Summary::::get(); let target = T::Target::get(); if summary.proportion_owed < target { let missing = target.saturating_sub(summary.proportion_owed); diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index ce4e3840aaa27..a5cc43b58cb50 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -111,17 +111,18 @@ parameter_types! { pub IgnoredIssuance: u64 = Balances::total_balance(&0); // Account zero is ignored. pub const NisPalletId: PalletId = PalletId(*b"py/nis "); pub static Target: Perquintill = Perquintill::zero(); + pub const MinReceipt: Perquintill = Perquintill::from_percent(1); + pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); } ord_parameter_types! { pub const One: u64 = 1; } -// TODO: Throttled thawing. - impl pallet_nis::Config for Test { type WeightInfo = (); type RuntimeEvent = RuntimeEvent; + type PalletId = NisPalletId; type Currency = Balances; type CurrencyBalance = >::Balance; type FundOrigin = frame_system::EnsureSigned; @@ -134,10 +135,11 @@ impl pallet_nis::Config for Test { type MaxQueueLen = ConstU32<3>; type FifoQueueLen = ConstU32<1>; type Period = ConstU64<3>; - type MinFreeze = ConstU64<2>; + type MinBid = ConstU64<2>; type IntakePeriod = ConstU64<2>; type MaxIntakeBids = ConstU32<2>; - type PalletId = NisPalletId; + type MinReceipt = MinReceipt; + type ThawThrottle = ThawThrottle; } // This function basically just builds a genesis storage key/value store according to diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 2391307b2e935..2c07ef9888981 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -30,6 +30,10 @@ use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; use sp_runtime::TokenError; +fn pot() -> u64 { + Balances::free_balance(&Nis::account_id()) +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -40,7 +44,12 @@ fn basic_setup_works() { } assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::zero(), index: 0 } + SummaryRecord { + proportion_owed: Perquintill::zero(), + index: 0, + last_period: 0, + thawed: Perquintill::zero() + } ); assert_eq!(QueueTotals::::get(), vec![(0, 0); 3]); }); @@ -178,10 +187,6 @@ fn retract_non_existent_item_fails() { }); } -fn pot() -> u64 { - Balances::free_balance(&Nis::account_id()) -} - #[test] fn basic_enlarge_works() { new_test_ext().execute_with(|| { @@ -201,7 +206,12 @@ fn basic_enlarge_works() { assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(10), index: 1 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(10), + index: 1, + last_period: 0, + thawed: Perquintill::zero() + } ); assert_eq!( Receipts::::get(0).unwrap(), @@ -236,7 +246,12 @@ fn enlarge_respects_bids_limit() { ); assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(20), + index: 2, + last_period: 0, + thawed: Perquintill::zero() + } ); }); } @@ -258,7 +273,12 @@ fn enlarge_respects_amount_limit_and_will_split() { ); assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(10), index: 1 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(10), + index: 1, + last_period: 0, + thawed: Perquintill::zero() + } ); }); } @@ -286,12 +306,19 @@ fn basic_thaw_works() { assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), Error::::NotOwner); assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_eq!(NisBalances::free_balance(1), 0); + assert_eq!(Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), None); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 100); assert_eq!(pot(), 0); assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::zero(), index: 1 } + SummaryRecord { + proportion_owed: Perquintill::zero(), + index: 1, + last_period: 0, + thawed: Perquintill::from_percent(10) + } ); assert_eq!(Receipts::::get(0), None); }); @@ -306,7 +333,17 @@ fn partial_thaw_works() { assert_eq!(pot(), 80); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1050000))); + assert_noop!( + Nis::thaw(RuntimeOrigin::signed(1), 0, Some(4_100_000)), + Error::::MakesDust + ); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1_050_000))); + + assert_eq!(NisBalances::free_balance(1), 3_150_000); + assert_eq!( + Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), + Some(Perquintill::from_rational(3_150_000u64, 21_000_000u64)), + ); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 40); @@ -320,7 +357,12 @@ fn partial_thaw_works() { assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::zero(), index: 1 } + SummaryRecord { + proportion_owed: Perquintill::zero(), + index: 1, + last_period: 0, + thawed: Perquintill::from_percent(20) + } ); assert_eq!(Receipts::::get(0), None); }); @@ -455,6 +497,8 @@ fn multiple_thaws_works() { run_to_block(4); assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 2, None), Error::::Throttled); + run_to_block(5); assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); assert_eq!(Balances::free_balance(1), 200); @@ -479,9 +523,12 @@ fn multiple_thaws_works_in_alternative_thaw_order() { run_to_block(4); assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Throttled); assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + run_to_block(5); + assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); }); @@ -522,14 +569,24 @@ fn enlargement_to_target_works() { ); assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(20), + index: 2, + last_period: 0, + thawed: Perquintill::zero() + } ); run_to_block(5); // No change assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(20), index: 2 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(20), + index: 2, + last_period: 0, + thawed: Perquintill::zero() + } ); run_to_block(6); @@ -544,14 +601,24 @@ fn enlargement_to_target_works() { ); assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(40), index: 4 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(40), + index: 4, + last_period: 0, + thawed: Perquintill::zero() + } ); run_to_block(8); // No change now. assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(40), index: 4 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(40), + index: 4, + last_period: 0, + thawed: Perquintill::zero() + } ); // Set target a bit higher to use up the remaining bid. @@ -566,7 +633,12 @@ fn enlargement_to_target_works() { assert_eq!( Summary::::get(), - SummaryRecord { proportion_owed: Perquintill::from_percent(50), index: 5 } + SummaryRecord { + proportion_owed: Perquintill::from_percent(50), + index: 5, + last_period: 0, + thawed: Perquintill::zero() + } ); }); } From c2fd94e64403d3b122e1c0807aad60fab4e0dc70 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 16:59:55 +0000 Subject: [PATCH 13/48] Remove todo --- frame/nis/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 9dbb116fab1c8..9d39f65e0cd39 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -112,8 +112,6 @@ where } } -//TODO: impl `ExchangeAsset`. - #[frame_support::pallet] pub mod pallet { pub use crate::weights::WeightInfo; From ec6b59497ef0ae97494c02b69ff6e131a71851ea Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 17:15:25 +0000 Subject: [PATCH 14/48] Docs --- frame/nis/src/lib.rs | 104 ++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 9d39f65e0cd39..65ecd3c729be2 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -222,8 +222,13 @@ pub mod pallet { /// the issuance with which we determine the thawed value of a given proportion. type IgnoredIssuance: Get>; + /// The accounting system for the fungible counterpart tokens. type Counterpart: FungibleMutate; + /// The system to convert an overall proportion of issuance into a number of fungible + /// counterpart tokens. + /// + /// In general it's best to use `WithMaximumOf`. type CounterpartAmount: ConvertBack< Perquintill, >::Balance, @@ -310,16 +315,16 @@ pub mod pallet { pub struct ReceiptRecord { /// The proportion of the effective total issuance. pub proportion: Perquintill, - /// The account to whom this bond belongs. + /// The account to whom this receipt belongs. pub who: AccountId, - /// The time after which this bond can be redeemed for the proportional amount of balance. + /// The time after which this receipt can be thawed. pub expiry: BlockNumber, } - /// An index for a bond. - pub type ActiveIndex = u32; + /// An index for a receipt. + pub type ReceiptIndex = u32; - /// Overall information package on the active bonds. + /// Overall information package on the outstanding receipts. /// /// The way of determining the net issuance (i.e. after factoring in all maturing frozen funds) /// is: @@ -331,10 +336,10 @@ pub mod pallet { Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] pub struct SummaryRecord { - /// The proportion of funds that the `frozen` balance represents to total issuance. + /// The total proportion over all outstanding receipts. pub proportion_owed: Perquintill, - /// The total number of bonds issued so far. - pub index: ActiveIndex, + /// The total number of receipts created so far. + pub index: ReceiptIndex, /// The amount (as a proportion of ETI) which has been thawed in this period so far. pub thawed: Perquintill, /// The current thaw period's beginning. @@ -350,7 +355,7 @@ pub mod pallet { pub type QueueTotals = StorageValue<_, BoundedVec<(u32, BalanceOf), T::QueueCount>, ValueQuery>; - /// The queues of bids ready to become bonds. Indexed by duration (in `Period`s). + /// The queues of bids. Indexed by duration (in `Period`s). #[pallet::storage] pub type Queues = StorageMap< _, @@ -360,14 +365,14 @@ pub mod pallet { ValueQuery, >; - /// Information relating to the bonds currently active. + /// Summary information over the general state. #[pallet::storage] pub type Summary = StorageValue<_, SummaryRecordOf, ValueQuery>; - /// The currently active bonds, indexed according to the order of creation. + /// The currently outstanding receipts, indexed according to the order of creation. #[pallet::storage] pub type Receipts = - StorageMap<_, Blake2_128Concat, ActiveIndex, ReceiptRecordOf, OptionQuery>; + StorageMap<_, Blake2_128Concat, ReceiptIndex, ReceiptRecordOf, OptionQuery>; #[pallet::genesis_config] #[derive(Default)] @@ -394,7 +399,7 @@ pub mod pallet { /// A bid was accepted. The balance may not be released until expiry. Issued { /// The identity of the receipt. - index: ActiveIndex, + index: ReceiptIndex, /// The block number at which the receipt may be thawed. expiry: T::BlockNumber, /// The owner of the receipt. @@ -404,10 +409,10 @@ pub mod pallet { /// The amount of funds which were debited from the owner. amount: BalanceOf, }, - /// An expired bond has been thawed. + /// An receipt has been (at least partially) thawed. Thawed { /// The identity of the receipt. - index: ActiveIndex, + index: ReceiptIndex, /// The owner. who: T::AccountId, /// The proportion of the effective total issuance by which the owner was debited. @@ -432,13 +437,13 @@ pub mod pallet { BidTooLow, /// Bond index is unknown. Unknown, - /// Not the owner of the bond. + /// Not the owner of the receipt. NotOwner, /// Bond not yet at expiry date. NotExpired, /// The given bid for retraction is not found. NotFound, - /// The portion supplied is beyond the value of the bond. + /// The portion supplied is beyond the value of the receipt. TooMuch, /// Not enough funds are held to pay out. Unfunded, @@ -463,15 +468,14 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Place a bid for a bond. + /// Place a bid. /// /// Origin must be Signed, and account must have at least `amount` in free balance. /// - /// - `amount`: The amount of the bid; these funds will be reserved. If the bid is - /// successfully elevated into a bond, then these funds will continue to be - /// reserved until the bond thaws. Must be at least `MinBid`. - /// - `duration`: The number of periods before which the bond may be thawed. - /// Must be greater than 1 and no more than `QueueCount`. + /// - `amount`: The amount of the bid; these funds will be reserved, and if/when + /// consolidated, removed. Must be at least `MinBid`. + /// - `duration`: The number of periods before which the newly consolidated bid may be + /// thawed. Must be greater than 1 and no more than `QueueCount`. /// /// Complexities: /// - `Queues[duration].len()` (just take max). @@ -578,17 +582,18 @@ pub mod pallet { Ok(()) } - /// Remove an active but expired bond. Reserved funds under bond are freed and balance is - /// adjusted to ensure that the newly freed amount is equivalent to the original amount in - /// terms of the proportion of effective total issued funds. - /// - /// Origin must be Signed and the account must be the owner of the bond of the given index. + /// Reduce or remove an outstanding receipt, placing the according proportion of funds into + /// the account of the owner. /// - /// - `index`: The index of the bond to be thawed. + /// - `origin`: Must be Signed and the account must be the owner of the receipt `index` as + /// well as any fungible counterpart. + /// - `index`: The index of the receipt. + /// - `portion`: If `Some`, then only the given portion of the receipt should be thawed. If + /// `None`, then all of it should be. #[pallet::weight(T::WeightInfo::thaw())] pub fn thaw( origin: OriginFor, - #[pallet::compact] index: ActiveIndex, + #[pallet::compact] index: ReceiptIndex, portion: Option<>::Balance>, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -653,23 +658,22 @@ pub mod pallet { /// Issuance information returned by `issuance()`. pub struct IssuanceInfo { - /// The balance held in reserve over all active bonds. + /// The balance held in reserve by this pallet instance. pub holdings: Balance, - /// The issuance not held in reserve for active bonds. Together with `reserved` this sums - /// to `Currency::total_issuance`. + /// The (non-ignored) issuance in the system, not including this pallet's account. pub other: Balance, - /// The balance that `reserved` is effectively worth, at present. This is not issued funds - /// and could be less than `reserved` (though in most cases should be greater). + /// The effective total issuance, hypothetically if all outstanding receipts were thawed at + /// present. pub effective: Balance, - /// The balance that `reserved` is effectively worth, at present. This is not issued funds - /// and could be less than `reserved` (though in most cases should be greater). + /// The amount needed to be the pallet instance's account in case all outstanding receipts + /// were thawed at present. pub required: Balance, } impl NonfungibleInspect for Pallet { - type ItemId = ActiveIndex; + type ItemId = ReceiptIndex; - fn owner(item: &ActiveIndex) -> Option { + fn owner(item: &ReceiptIndex) -> Option { Receipts::::get(item).map(|r| r.who) } @@ -684,7 +688,7 @@ pub mod pallet { } impl NonfungibleTransfer for Pallet { - fn transfer(index: &ActiveIndex, destination: &T::AccountId) -> DispatchResult { + fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult { let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; item.who = destination.clone(); Receipts::::insert(index, item); @@ -701,16 +705,19 @@ pub mod pallet { T::PalletId::get().into_account_truncating() } - /// Returns information on the issuance of bonds. - /// - /// Also provides the summary and this pallet's account ID. + /// Returns information on the issuance within the system. pub fn issuance() -> IssuanceInfo> { Self::issuance_with(&Self::account_id(), &Summary::::get()) } - /// Returns information on the issuance of bonds. + /// Returns information on the issuance within the system /// - /// Also provides the summary and this pallet's account ID. + /// This function is equivalent to `issuance`, except that it accepts arguments rather than + /// queries state. If the arguments are already known, then this may be slightly more + /// performant. + /// + /// - `our_account`: The value returned by `Self::account_id()`. + /// - `summary`: The value returned by `Summary::::get()`. pub fn issuance_with( our_account: &T::AccountId, summary: &SummaryRecordOf, @@ -725,8 +732,11 @@ pub mod pallet { IssuanceInfo { holdings, other, effective, required } } - /// Attempt to enlarge our bond-set from bids in order to satisfy our desired target amount - /// of funds frozen into bonds. + /// Process some bids into receipts in line with the pallet's configuration, especially + /// `Target`. + /// + /// Returns the weight used. + // TODO: Accept max_weight, not max_bids. pub fn pursue_target(max_bids: u32) -> Weight { let summary: SummaryRecordOf = Summary::::get(); let target = T::Target::get(); From 1442179b1a268f9e05474c847ae3eee71077eb4e Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 3 Nov 2022 18:29:22 +0000 Subject: [PATCH 15/48] Fix benchmarks --- frame/nis/src/benchmarking.rs | 79 ++++++++++++++++++----------------- frame/nis/src/lib.rs | 10 ++--- frame/nis/src/weights.rs | 13 ------ 3 files changed, 44 insertions(+), 58 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 4b2a58a53f332..9a1dcc8adb55c 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -21,13 +21,10 @@ use super::*; use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::{ - dispatch::UnfilteredDispatchable, - traits::{Currency, EnsureOrigin, Get}, -}; +use frame_support::traits::{Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; -use sp_runtime::traits::{Bounded, Zero}; +use sp_runtime::traits::{Bounded, One, Zero}; use sp_std::prelude::*; use crate::Pallet as Nis; @@ -41,11 +38,11 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..l { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; } - }: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get() * BalanceOf::::from(2u32), 1) + }: _(RawOrigin::Signed(caller.clone()), T::MinBid::get() * BalanceOf::::from(2u32), 1) verify { - assert_eq!(QueueTotals::::get()[0], (l + 1, T::MinFreeze::get() * BalanceOf::::from(l + 2))); + assert_eq!(QueueTotals::::get()[0], (l + 1, T::MinBid::get() * BalanceOf::::from(l + 2))); } place_bid_max { @@ -53,13 +50,13 @@ benchmarks! { let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..T::MaxQueueLen::get() { - Nis::::place_bid(origin.clone().into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(origin.clone().into(), T::MinBid::get(), 1)?; } - }: place_bid(origin, T::MinFreeze::get() * BalanceOf::::from(2u32), 1) + }: place_bid(origin, T::MinBid::get() * BalanceOf::::from(2u32), 1) verify { assert_eq!(QueueTotals::::get()[0], ( T::MaxQueueLen::get(), - T::MinFreeze::get() * BalanceOf::::from(T::MaxQueueLen::get() + 1), + T::MinBid::get() * BalanceOf::::from(T::MaxQueueLen::get() + 1), )); } @@ -68,64 +65,68 @@ benchmarks! { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); for i in 0..l { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; } - }: _(RawOrigin::Signed(caller.clone()), T::MinFreeze::get(), 1) + }: _(RawOrigin::Signed(caller.clone()), T::MinBid::get(), 1) verify { - assert_eq!(QueueTotals::::get()[0], (l - 1, T::MinFreeze::get() * BalanceOf::::from(l - 1))); + assert_eq!(QueueTotals::::get()[0], (l - 1, T::MinBid::get() * BalanceOf::::from(l - 1))); } - set_target { - let origin = T::AdminOrigin::successful_origin(); - }: _(origin, Default::default()) - verify {} + fund_deficit { + let origin = T::FundOrigin::successful_origin(); + let caller: T::AccountId = whitelisted_caller(); + let bid = T::MinBid::get().max(One::one()); + T::Currency::make_free_balance_be(&caller, bid); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; + Nis::::enlarge(bid, 1); + let original = T::Currency::free_balance(&Nis::::account_id()); + T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); + }: _(origin) + verify { + assert_eq!(original, T::Currency::free_balance(&Nis::::account_id())); + } thaw { let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(3u32)); - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; - Nis::::enlarge(T::MinFreeze::get() * BalanceOf::::from(2u32), 2); - Active::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); - }: _(RawOrigin::Signed(caller.clone()), 0) + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::enlarge(T::MinBid::get() * BalanceOf::::from(2u32), 2); + Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); + }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { - assert!(Active::::get(0).is_none()); + assert!(Receipts::::get(0).is_none()); } pursue_target_noop { - }: { Nis::::pursue_target(0) } + }: { Nis::::pursue_target(0, Zero::zero()) } pursue_target_per_item { // bids taken let b in 0..T::MaxQueueLen::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(b + 1)); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(b + 1)); for _ in 0..b { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; } - - Call::::set_target { target: Perquintill::from_percent(100) } - .dispatch_bypass_filter(T::AdminOrigin::successful_origin())?; - - }: { Nis::::pursue_target(b) } + let target = Perquintill::one(); + }: { Nis::::pursue_target(b, target) } pursue_target_per_queue { // total queues hit let q in 0..T::QueueCount::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinFreeze::get() * BalanceOf::::from(q + 1)); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(q + 1)); for i in 0..q { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinFreeze::get(), i + 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), i + 1)?; } - Call::::set_target { target: Perquintill::from_percent(100) } - .dispatch_bypass_filter(T::AdminOrigin::successful_origin())?; - - }: { Nis::::pursue_target(q) } + let target = Perquintill::one(); + }: { Nis::::pursue_target(q, target) } impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 65ecd3c729be2..970d817354775 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -459,7 +459,7 @@ pub mod pallet { impl Hooks> for Pallet { fn on_initialize(n: T::BlockNumber) -> Weight { if (n % T::IntakePeriod::get()).is_zero() { - Self::pursue_target(T::MaxIntakeBids::get()) + Self::pursue_target(T::MaxIntakeBids::get(), T::Target::get()) } else { Weight::zero() } @@ -657,6 +657,7 @@ pub mod pallet { } /// Issuance information returned by `issuance()`. + #[derive(RuntimeDebug)] pub struct IssuanceInfo { /// The balance held in reserve by this pallet instance. pub holdings: Balance, @@ -732,14 +733,11 @@ pub mod pallet { IssuanceInfo { holdings, other, effective, required } } - /// Process some bids into receipts in line with the pallet's configuration, especially - /// `Target`. + /// Process some bids into receipts in line with the pallet's configuration. /// /// Returns the weight used. - // TODO: Accept max_weight, not max_bids. - pub fn pursue_target(max_bids: u32) -> Weight { + pub fn pursue_target(max_bids: u32, target: Perquintill) -> Weight { let summary: SummaryRecordOf = Summary::::get(); - let target = T::Target::get(); if summary.proportion_owed < target { let missing = target.saturating_sub(summary.proportion_owed); let issuance = Self::issuance_with(&Self::account_id(), &summary); diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index fa7caa26ab7be..e8f1e3f61ce11 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -47,7 +47,6 @@ pub trait WeightInfo { fn place_bid(l: u32, ) -> Weight; fn place_bid_max() -> Weight; fn retract_bid(l: u32, ) -> Weight; - fn set_target() -> Weight; fn thaw() -> Weight; fn fund_deficit() -> Weight; fn pursue_target_noop() -> Weight; @@ -83,12 +82,6 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Nis ActiveTotal (r:1 w:1) - fn set_target() -> Weight { - Weight::from_ref_time(5_026_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } // Storage: Nis Active (r:1 w:1) // Storage: Nis ActiveTotal (r:1 w:1) fn thaw() -> Weight { @@ -162,12 +155,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Nis ActiveTotal (r:1 w:1) - fn set_target() -> Weight { - Weight::from_ref_time(5_026_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } // Storage: Nis Active (r:1 w:1) // Storage: Nis ActiveTotal (r:1 w:1) fn thaw() -> Weight { From 86ceb01dd3b7486638dbd14d586889df128cc131 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 4 Nov 2022 17:12:28 +0000 Subject: [PATCH 16/48] Building --- frame/nis/src/benchmarking.rs | 6 +- frame/nis/src/lib.rs | 266 +++++++++++++++++++++------------- frame/nis/src/mock.rs | 5 +- frame/nis/src/tests.rs | 34 +++-- frame/nis/src/weights.rs | 28 ++-- 5 files changed, 203 insertions(+), 136 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 9a1dcc8adb55c..742139e6322e8 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -98,10 +98,10 @@ benchmarks! { assert!(Receipts::::get(0).is_none()); } - pursue_target_noop { + process_queues { }: { Nis::::pursue_target(0, Zero::zero()) } - pursue_target_per_item { + process_queue { // bids taken let b in 0..T::MaxQueueLen::get(); @@ -114,7 +114,7 @@ benchmarks! { let target = Perquintill::one(); }: { Nis::::pursue_target(b, target) } - pursue_target_per_queue { + process_bid { // total queues hit let q in 0..T::QueueCount::get(); diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 970d817354775..23a2f7bd31260 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -143,7 +143,10 @@ pub mod pallet { ::AccountId, ::BlockNumber, >; + type IssuanceInfoOf = IssuanceInfo>; type SummaryRecordOf = SummaryRecord<::BlockNumber>; + type BidOf = Bid, ::AccountId>; + type QueueTotalsTypeOf = BoundedVec<(u32, BalanceOf), ::QueueCount>; pub struct NoCounterpart(sp_std::marker::PhantomData); impl FungibleInspect for NoCounterpart { @@ -286,7 +289,7 @@ pub mod pallet { /// larger value here means less of the block available for transactions should there be a /// glut of bids. #[pallet::constant] - type MaxIntakeBids: Get; + type MaxIntakeWeight: Get; /// The maximum proportion which may be thawed and the period over which it is reset. #[pallet::constant] @@ -352,8 +355,7 @@ pub mod pallet { /// The vector is indexed by duration in `Period`s, offset by one, so information on the queue /// whose duration is one `Period` would be storage `0`. #[pallet::storage] - pub type QueueTotals = - StorageValue<_, BoundedVec<(u32, BalanceOf), T::QueueCount>, ValueQuery>; + pub type QueueTotals = StorageValue<_, QueueTotalsTypeOf, ValueQuery>; /// The queues of bids. Indexed by duration (in `Period`s). #[pallet::storage] @@ -361,7 +363,7 @@ pub mod pallet { _, Blake2_128Concat, u32, - BoundedVec, T::AccountId>, T::MaxQueueLen>, + BoundedVec, T::MaxQueueLen>, ValueQuery, >; @@ -455,14 +457,36 @@ pub mod pallet { MakesDust, } + pub(crate) struct WeightCounter { + pub(crate) used: Weight, + pub(crate) limit: Weight, + } + impl WeightCounter { + fn check_accrue(&mut self, w: Weight) -> bool { + let test = self.used.saturating_add(w); + if test.any_gt(self.limit) { + false + } else { + self.used = test; + true + } + } + fn can_accrue(&mut self, w: Weight) -> bool { + self.used.saturating_add(w).all_lte(self.limit) + } + } + #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(n: T::BlockNumber) -> Weight { + let mut weight_counter = + WeightCounter { used: Weight::zero(), limit: T::MaxIntakeWeight::get() }; if (n % T::IntakePeriod::get()).is_zero() { - Self::pursue_target(T::MaxIntakeBids::get(), T::Target::get()) - } else { - Weight::zero() + if weight_counter.check_accrue(T::WeightInfo::process_queues()) { + Self::process_queues(T::Target::get(), T::QueueCount::get(), u32::max_value(), &mut weight_counter) + } } + weight_counter.used } } @@ -733,110 +757,154 @@ pub mod pallet { IssuanceInfo { holdings, other, effective, required } } - /// Process some bids into receipts in line with the pallet's configuration. + /// Process some bids into receipts up to a `target` total of all receipts. /// - /// Returns the weight used. - pub fn pursue_target(max_bids: u32, target: Perquintill) -> Weight { - let summary: SummaryRecordOf = Summary::::get(); - if summary.proportion_owed < target { - let missing = target.saturating_sub(summary.proportion_owed); - let issuance = Self::issuance_with(&Self::account_id(), &summary); - - let intake = missing * issuance.effective; - - let (bids_taken, queues_hit) = Self::enlarge(intake, max_bids); - let first_from_each_queue = T::WeightInfo::pursue_target_per_queue(queues_hit); - let rest_from_each_queue = T::WeightInfo::pursue_target_per_item(bids_taken) - .saturating_sub(T::WeightInfo::pursue_target_per_item(queues_hit)); - first_from_each_queue + rest_from_each_queue - } else { - T::WeightInfo::pursue_target_noop() + /// Touch at most `max_queues`. + /// + /// Return the weight used. + pub(crate) fn process_queues( + target: Perquintill, + max_queues: u32, + max_bids: u32, + weight: &mut WeightCounter, + ) { + let mut summary: SummaryRecordOf = Summary::::get(); + if summary.proportion_owed >= target { + return } - } - /// Freeze additional funds from queue of bids up to `amount`. Use at most `max_bids` - /// from the queue. - /// - /// Return the number of bids taken and the number of distinct queues taken from. - pub fn enlarge(amount: BalanceOf, max_bids: u32) -> (u32, u32) { - let mut remaining = amount; - let mut bids_taken = 0; - let mut queues_hit = 0; let now = frame_system::Pallet::::block_number(); - - let mut summary = Summary::::get(); - let mut queue_totals = QueueTotals::::get(); let our_account = Self::account_id(); - let issuance = Self::issuance_with(&our_account, &summary); + let issuance: IssuanceInfoOf = Self::issuance_with(&our_account, &summary); + let mut remaining = target.saturating_sub(summary.proportion_owed) * issuance.effective; - for duration in (1..=T::QueueCount::get()).rev() { - if queue_totals[duration as usize - 1].0 == 0 { - continue + let mut queues_hit = 0; + let mut bids_hit = 0; + let mut totals = QueueTotals::::get(); + let count = T::QueueCount::get(); + for duration in (1..=count).rev() { + if totals[duration as usize - 1].0.is_zero() { break } + if remaining.is_zero() || queues_hit >= max_queues + || !weight.check_accrue(T::WeightInfo::process_queue()) + // No point trying to process a queue if we can't process a single bid. + || !weight.can_accrue(T::WeightInfo::process_bid()) + { + break } - let queue_index = duration as usize - 1; - let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into())); - let mut queue = Queues::::get(&duration); - while let Some(mut bid) = queue.pop() { - if remaining < bid.amount { - let overflow = bid.amount - remaining; - bid.amount = remaining; - queue - .try_push(Bid { amount: overflow, who: bid.who.clone() }) - .expect("just popped, so there must be space. qed"); - } - let amount = - bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); - if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { - continue - } - - // Can never overflow due to block above. - remaining.saturating_reduce(amount); - // Should never underflow since it should track the total of the - // bids exactly, but we'll be defensive. - queue_totals[queue_index].1 = - queue_totals[queue_index].1.defensive_saturating_sub(amount); - - // Now to activate the bid... - let n = amount; - let d = issuance.effective; - let proportion = Perquintill::from_rational(n, d); - let who = bid.who; - let index = summary.index; - summary.proportion_owed.defensive_saturating_accrue(proportion); - summary.index += 1; - - let e = Event::Issued { - index, - expiry, - who: who.clone(), - amount, - proportion: proportion.clone(), - }; - Self::deposit_event(e); - let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; - Receipts::::insert(index, receipt); - // issue the fungible counterpart - let fung_eq = T::CounterpartAmount::convert(proportion); - let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); + let b = Self::process_queue( + duration, + now, + &our_account, + &issuance, + max_bids.saturating_sub(bids_hit), + &mut remaining, + &mut totals[duration as usize - 1], + &mut summary, + weight, + ); - bids_taken += 1; + bids_hit.saturating_accrue(b); + queues_hit.saturating_inc(); + } + QueueTotals::::put(&totals); + Summary::::put(&summary); + } - if remaining.is_zero() || bids_taken == max_bids { - break - } - } - queues_hit += 1; - queue_totals[queue_index].0 = queue.len() as u32; - Queues::::insert(&duration, &queue); - if remaining.is_zero() || bids_taken == max_bids { - break + pub(crate) fn process_queue( + duration: u32, + now: T::BlockNumber, + our_account: &T::AccountId, + issuance: &IssuanceInfo>, + max_bids: u32, + remaining: &mut BalanceOf, + queue_total: &mut (u32, BalanceOf), + summary: &mut SummaryRecordOf, + weight: &mut WeightCounter, + ) -> u32 { + let mut queue: BoundedVec, _> = Queues::::get(&duration); + let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into())); + let mut count = 0; + + while count < max_bids && !queue.is_empty() && !remaining.is_zero() + && weight.check_accrue(T::WeightInfo::process_bid()) + { + let bid = match queue.pop() { + Some(b) => b, + None => break, + }; + if let Some(bid) = Self::process_bid( + bid, + expiry, + our_account, + issuance, + remaining, + &mut queue_total.1, + summary, + ) { + queue + .try_push(bid) + .expect("just popped, so there must be space. qed"); } + count.saturating_inc(); } - QueueTotals::::put(&queue_totals); - Summary::::put(&summary); - (bids_taken, queues_hit) + queue_total.0 = queue.len() as u32; + Queues::::insert(&duration, &queue); + count + } + + pub(crate) fn process_bid( + mut bid: BidOf, + expiry: T::BlockNumber, + our_account: &T::AccountId, + issuance: &IssuanceInfo>, + remaining: &mut BalanceOf, + queue_amount: &mut BalanceOf, + summary: &mut SummaryRecordOf, + ) -> Option> { + let result = if *remaining < bid.amount { + let overflow = bid.amount - *remaining; + bid.amount = *remaining; + Some(Bid { amount: overflow, who: bid.who.clone() }) + } else { + None + }; + let amount = + bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); + if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { + return result + } + + // Can never overflow due to block above. + remaining.saturating_reduce(amount); + // Should never underflow since it should track the total of the + // bids exactly, but we'll be defensive. + queue_amount.defensive_saturating_reduce(amount); + + // Now to activate the bid... + let n = amount; + let d = issuance.effective; + let proportion = Perquintill::from_rational(n, d); + let who = bid.who; + let index = summary.index; + summary.proportion_owed.defensive_saturating_accrue(proportion); + summary.index += 1; + + let e = Event::Issued { + index, + expiry, + who: who.clone(), + amount, + proportion: proportion.clone(), + }; + Self::deposit_event(e); + let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; + Receipts::::insert(index, receipt); + + // issue the fungible counterpart + let fung_eq = T::CounterpartAmount::convert(proportion); + let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); + result } } } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index a5cc43b58cb50..1218d74b074b4 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -25,7 +25,7 @@ use frame_support::{ ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim, }, - PalletId, + PalletId, weights::Weight, }; use pallet_balances::{Instance1, Instance2}; use sp_core::{ConstU128, H256}; @@ -113,6 +113,7 @@ parameter_types! { pub static Target: Perquintill = Perquintill::zero(); pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); + pub const MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000); } ord_parameter_types! { @@ -137,7 +138,7 @@ impl pallet_nis::Config for Test { type Period = ConstU64<3>; type MinBid = ConstU64<2>; type IntakePeriod = ConstU64<2>; - type MaxIntakeBids = ConstU32<2>; + type MaxIntakeWeight = MaxIntakeWeight; type MinReceipt = MinReceipt; type ThawThrottle = ThawThrottle; } diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index 2c07ef9888981..df4f3b0588ec7 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -24,16 +24,24 @@ use frame_support::{ traits::{ nonfungible::{Inspect, Transfer}, Currency, - }, + }, weights::Weight, }; use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; -use sp_runtime::TokenError; +use sp_runtime::{TokenError, traits::Bounded, Saturating}; fn pot() -> u64 { Balances::free_balance(&Nis::account_id()) } +fn enlarge(amount: u64, max_bids: u32) { + let mut weight_counter = WeightCounter { used: Weight::zero(), limit: Weight::max_value() }; + let summary: SummaryRecord = Summary::::get(); + let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective); + let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed); + Nis::process_queues(target, u32::max_value(), max_bids, &mut weight_counter); +} + #[test] fn basic_setup_works() { new_test_ext().execute_with(|| { @@ -193,7 +201,7 @@ fn basic_enlarge_works() { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); - Nis::enlarge(40, 2); + enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount assert_eq!(Balances::reserved_balance(1), 40); @@ -228,7 +236,7 @@ fn enlarge_respects_bids_limit() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 2)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 40, 3)); - Nis::enlarge(100, 2); + enlarge(100, 2); // Should have taken 4/3 and 2/2, then stopped because it's only allowed 2. assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); @@ -261,7 +269,7 @@ fn enlarge_respects_amount_limit_and_will_split() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); - Nis::enlarge(40, 2); + enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); @@ -293,7 +301,7 @@ fn basic_thaw_works() { assert_eq!(Balances::reserved_balance(1), 40); assert_eq!(pot(), 0); - Nis::enlarge(40, 1); + enlarge(40, 1); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 60); assert_eq!(Balances::reserved_balance(1), 0); @@ -329,7 +337,7 @@ fn partial_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); - Nis::enlarge(80, 1); + enlarge(80, 1); assert_eq!(pot(), 80); run_to_block(4); @@ -373,7 +381,7 @@ fn thaw_respects_transfers() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - Nis::enlarge(40, 1); + enlarge(40, 1); run_to_block(4); assert_eq!(Nis::owner(&0), Some(1)); @@ -399,7 +407,7 @@ fn thaw_when_issuance_higher_works() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Nis::enlarge(100, 1); + enlarge(100, 1); assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) @@ -438,7 +446,7 @@ fn thaw_with_ignored_issuance_works() { Balances::make_free_balance_be(&0, 200); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Nis::enlarge(100, 1); + enlarge(100, 1); // Account zero transfers 50 into everyone else's accounts. assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50)); @@ -464,7 +472,7 @@ fn thaw_when_issuance_lower_works() { new_test_ext().execute_with(|| { run_to_block(1); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); - Nis::enlarge(100, 1); + enlarge(100, 1); // Everybody else's balances goes down by 25% Balances::make_free_balance_be(&2, 75); @@ -486,7 +494,7 @@ fn multiple_thaws_works() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); - Nis::enlarge(200, 3); + enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); @@ -513,7 +521,7 @@ fn multiple_thaws_works_in_alternative_thaw_order() { assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); - Nis::enlarge(200, 3); + enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index e8f1e3f61ce11..5e55fcf529027 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -49,9 +49,9 @@ pub trait WeightInfo { fn retract_bid(l: u32, ) -> Weight; fn thaw() -> Weight; fn fund_deficit() -> Weight; - fn pursue_target_noop() -> Weight; - fn pursue_target_per_item(b: u32, ) -> Weight; - fn pursue_target_per_queue(q: u32, ) -> Weight; + fn process_queues() -> Weight; + fn process_queue() -> Weight; + fn process_bid() -> Weight; } /// Weights for pallet_nis using the Substrate node and recommended hardware. @@ -97,7 +97,7 @@ impl WeightInfo for SubstrateWeight { .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Nis ActiveTotal (r:1 w:0) - fn pursue_target_noop() -> Weight { + fn process_queues() -> Weight { Weight::from_ref_time(1_663_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) } @@ -105,26 +105,21 @@ impl WeightInfo for SubstrateWeight { // Storage: Nis QueueTotals (r:1 w:1) // Storage: Nis Queues (r:1 w:1) // Storage: Nis Active (r:0 w:1) - fn pursue_target_per_item(b: u32, ) -> Weight { + fn process_queue() -> Weight { Weight::from_ref_time(40_797_000 as u64) // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(4_122_000 as u64).saturating_mul(b as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(b as u64))) } // Storage: Nis ActiveTotal (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) // Storage: Nis Queues (r:1 w:1) // Storage: Nis Active (r:0 w:1) - fn pursue_target_per_queue(q: u32, ) -> Weight { + fn process_bid() -> Weight { Weight::from_ref_time(14_944_000 as u64) // Standard Error: 6_000 - .saturating_add(Weight::from_ref_time(8_135_000 as u64).saturating_mul(q as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(q as u64))) .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(q as u64))) } } @@ -170,7 +165,7 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Nis ActiveTotal (r:1 w:0) - fn pursue_target_noop() -> Weight { + fn process_queues() -> Weight { Weight::from_ref_time(1_663_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) } @@ -178,25 +173,20 @@ impl WeightInfo for () { // Storage: Nis QueueTotals (r:1 w:1) // Storage: Nis Queues (r:1 w:1) // Storage: Nis Active (r:0 w:1) - fn pursue_target_per_item(b: u32, ) -> Weight { + fn process_queue() -> Weight { Weight::from_ref_time(40_797_000 as u64) // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(4_122_000 as u64).saturating_mul(b as u64)) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(b as u64))) } // Storage: Nis ActiveTotal (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) // Storage: Nis Queues (r:1 w:1) // Storage: Nis Active (r:0 w:1) - fn pursue_target_per_queue(q: u32, ) -> Weight { + fn process_bid() -> Weight { Weight::from_ref_time(14_944_000 as u64) // Standard Error: 6_000 - .saturating_add(Weight::from_ref_time(8_135_000 as u64).saturating_mul(q as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(q as u64))) .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(q as u64))) } } From 54609bedf866a3607b2b5a0b329afc4d4a6a2128 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 4 Nov 2022 17:23:52 +0000 Subject: [PATCH 17/48] Tests work --- frame/nis/src/lib.rs | 33 +++++++++++++++++---------------- frame/nis/src/mock.rs | 5 +++-- frame/nis/src/tests.rs | 11 ++++++++--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 23a2f7bd31260..a03dcadb1b38f 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -359,13 +359,8 @@ pub mod pallet { /// The queues of bids. Indexed by duration (in `Period`s). #[pallet::storage] - pub type Queues = StorageMap< - _, - Blake2_128Concat, - u32, - BoundedVec, T::MaxQueueLen>, - ValueQuery, - >; + pub type Queues = + StorageMap<_, Blake2_128Concat, u32, BoundedVec, T::MaxQueueLen>, ValueQuery>; /// Summary information over the general state. #[pallet::storage] @@ -483,7 +478,12 @@ pub mod pallet { WeightCounter { used: Weight::zero(), limit: T::MaxIntakeWeight::get() }; if (n % T::IntakePeriod::get()).is_zero() { if weight_counter.check_accrue(T::WeightInfo::process_queues()) { - Self::process_queues(T::Target::get(), T::QueueCount::get(), u32::max_value(), &mut weight_counter) + Self::process_queues( + T::Target::get(), + T::QueueCount::get(), + u32::max_value(), + &mut weight_counter, + ) } } weight_counter.used @@ -783,7 +783,9 @@ pub mod pallet { let mut totals = QueueTotals::::get(); let count = T::QueueCount::get(); for duration in (1..=count).rev() { - if totals[duration as usize - 1].0.is_zero() { break } + if totals[duration as usize - 1].0.is_zero() { + continue + } if remaining.is_zero() || queues_hit >= max_queues || !weight.check_accrue(T::WeightInfo::process_queue()) // No point trying to process a queue if we can't process a single bid. @@ -826,8 +828,10 @@ pub mod pallet { let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into())); let mut count = 0; - while count < max_bids && !queue.is_empty() && !remaining.is_zero() - && weight.check_accrue(T::WeightInfo::process_bid()) + while count < max_bids && + !queue.is_empty() && + !remaining.is_zero() && + weight.check_accrue(T::WeightInfo::process_bid()) { let bid = match queue.pop() { Some(b) => b, @@ -842,9 +846,7 @@ pub mod pallet { &mut queue_total.1, summary, ) { - queue - .try_push(bid) - .expect("just popped, so there must be space. qed"); + queue.try_push(bid).expect("just popped, so there must be space. qed"); } count.saturating_inc(); } @@ -869,8 +871,7 @@ pub mod pallet { } else { None }; - let amount = - bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); + let amount = bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { return result } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 1218d74b074b4..e7a34cec103e8 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -25,7 +25,8 @@ use frame_support::{ ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, StorageMapShim, }, - PalletId, weights::Weight, + weights::Weight, + PalletId, }; use pallet_balances::{Instance1, Instance2}; use sp_core::{ConstU128, H256}; @@ -113,7 +114,7 @@ parameter_types! { pub static Target: Perquintill = Perquintill::zero(); pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); - pub const MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000); + pub static MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000_000); } ord_parameter_types! { diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index df4f3b0588ec7..ff4166461c909 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -24,11 +24,12 @@ use frame_support::{ traits::{ nonfungible::{Inspect, Transfer}, Currency, - }, weights::Weight, + }, + weights::Weight, }; use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; -use sp_runtime::{TokenError, traits::Bounded, Saturating}; +use sp_runtime::{traits::Bounded, Saturating, TokenError}; fn pot() -> u64 { Balances::free_balance(&Nis::account_id()) @@ -546,6 +547,10 @@ fn multiple_thaws_works_in_alternative_thaw_order() { fn enlargement_to_target_works() { new_test_ext().execute_with(|| { run_to_block(2); + let w = <() as WeightInfo>::process_queues() + + <() as WeightInfo>::process_queue() + + (<() as WeightInfo>::process_bid() * 2); + super::mock::MaxIntakeWeight::set(w); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 2)); assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); @@ -581,7 +586,7 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(20), index: 2, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), } ); From 085996beed0c78fa59b1b720dc193ec028fb9750 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 5 Nov 2022 15:16:02 +0000 Subject: [PATCH 18/48] New benchmarks --- frame/nis/src/benchmarking.rs | 97 +++++++++++++++++++++++++---------- frame/nis/src/lib.rs | 9 ++-- frame/nis/src/tests.rs | 6 +-- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 742139e6322e8..f6f2a4038464c 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,18 +20,39 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{benchmarks, whitelisted_caller, account}; use frame_support::traits::{Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; -use sp_runtime::traits::{Bounded, One, Zero}; +use sp_runtime::{traits::{Bounded, One, Zero}, DispatchError}; use sp_std::prelude::*; use crate::Pallet as Nis; +const SEED: u32 = 0; + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +fn fill_queues() -> Result<(), DispatchError> { + // filling queues involves filling the first queue entirely and placing a single item in all + // other queues. + + let queues = T::QueueCount::get(); + let bids = T::MaxQueueLen::get(); + + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(queues + bids)); + + for _ in 0..bids { + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + } + for d in 1..queues { + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1 + d)?; + } + Ok(()) +} + benchmarks! { place_bid { let l in 0..(T::MaxQueueLen::get() - 1); @@ -78,7 +99,7 @@ benchmarks! { let bid = T::MinBid::get().max(One::one()); T::Currency::make_free_balance_be(&caller, bid); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; - Nis::::enlarge(bid, 1); + //Nis::::enlarge(bid, 1); let original = T::Currency::free_balance(&Nis::::account_id()); T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) @@ -91,7 +112,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - Nis::::enlarge(T::MinBid::get() * BalanceOf::::from(2u32), 2); + //Nis::::enlarge(T::MinBid::get() * BalanceOf::::from(2u32), 2); Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { @@ -99,34 +120,54 @@ benchmarks! { } process_queues { - }: { Nis::::pursue_target(0, Zero::zero()) } + fill_queues::()?; + }: { + Nis::::process_queues( + Perquintill::one(), + Zero::zero(), + u32::max_value(), + &mut WeightCounter::unlimited(), + ) + } process_queue { - // bids taken - let b in 0..T::MaxQueueLen::get(); - - let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(b + 1)); - - for _ in 0..b { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - } - let target = Perquintill::one(); - }: { Nis::::pursue_target(b, target) } + let our_account = Nis::::account_id(); + let issuance = Nis::::issuance(); + let mut summary = Summary::::get(); + }: { + Nis::::process_queue( + 1u32, + 1u32.into(), + &our_account, + &issuance, + 0, + &mut Bounded::max_value(), + &mut (T::MaxQueueLen::get(), Bounded::max_value()), + &mut summary, + &mut WeightCounter::unlimited(), + ) + } process_bid { - // total queues hit - let q in 0..T::QueueCount::get(); - - let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(q + 1)); - - for i in 0..q { - Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), i + 1)?; - } - - let target = Perquintill::one(); - }: { Nis::::pursue_target(q, target) } + let who = account::("bidder", 0, SEED); + let bid = Bid { + amount: T::MinBid::get(), + who, + }; + let our_account = Nis::::account_id(); + let issuance = Nis::::issuance(); + let mut summary = Summary::::get(); + }: { + Nis::::process_bid( + bid, + 2u32.into(), + &our_account, + &issuance, + &mut Bounded::max_value(), + &mut Bounded::max_value(), + &mut summary, + ) + } impl_benchmark_test_suite!(Nis, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index a03dcadb1b38f..bfce90a12cac5 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -129,7 +129,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, ConvertBack, Saturating, Zero}, + traits::{AccountIdConversion, Convert, ConvertBack, Saturating, Zero, Bounded}, TokenError, }; use sp_std::prelude::*; @@ -457,6 +457,9 @@ pub mod pallet { pub(crate) limit: Weight, } impl WeightCounter { + pub fn unlimited() -> Self { + WeightCounter { used: Weight::zero(), limit: Weight::max_value() } + } fn check_accrue(&mut self, w: Weight) -> bool { let test = self.used.saturating_add(w); if test.any_gt(self.limit) { @@ -508,7 +511,7 @@ pub mod pallet { origin: OriginFor, #[pallet::compact] amount: BalanceOf, duration: u32, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { let who = ensure_signed(origin)?; ensure!(amount >= T::MinBid::get(), Error::::AmountTooSmall); @@ -550,7 +553,7 @@ pub mod pallet { }); Self::deposit_event(Event::BidPlaced { who, amount, duration }); - Ok(().into()) + Ok(()) } /// Retract a previously placed bid. diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index ff4166461c909..da5c56af46788 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -25,22 +25,20 @@ use frame_support::{ nonfungible::{Inspect, Transfer}, Currency, }, - weights::Weight, }; use pallet_balances::{Error as BalancesError, Instance1}; use sp_arithmetic::Perquintill; -use sp_runtime::{traits::Bounded, Saturating, TokenError}; +use sp_runtime::{Saturating, TokenError}; fn pot() -> u64 { Balances::free_balance(&Nis::account_id()) } fn enlarge(amount: u64, max_bids: u32) { - let mut weight_counter = WeightCounter { used: Weight::zero(), limit: Weight::max_value() }; let summary: SummaryRecord = Summary::::get(); let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective); let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed); - Nis::process_queues(target, u32::max_value(), max_bids, &mut weight_counter); + Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited()); } #[test] From 077afbe1a63db00322b9efe5c6e3665379c8e5e9 Mon Sep 17 00:00:00 2001 From: Gav Date: Sat, 5 Nov 2022 16:15:57 +0000 Subject: [PATCH 19/48] Benchmarking tests --- frame/nis/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index f6f2a4038464c..c54b65aecbb69 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -99,7 +99,7 @@ benchmarks! { let bid = T::MinBid::get().max(One::one()); T::Currency::make_free_balance_be(&caller, bid); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; - //Nis::::enlarge(bid, 1); + Nis::::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited()); let original = T::Currency::free_balance(&Nis::::account_id()); T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) @@ -112,7 +112,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; - //Nis::::enlarge(T::MinBid::get() * BalanceOf::::from(2u32), 2); + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); }: _(RawOrigin::Signed(caller.clone()), 0, None) verify { From 3ca691858ef60b94291f5080ace245f1e78b9563 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 5 Nov 2022 21:05:41 +0100 Subject: [PATCH 20/48] Test new defensive_saturating_* functions Signed-off-by: Oliver Tale-Yazdi --- frame/support/src/traits/misc.rs | 88 ++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/frame/support/src/traits/misc.rs b/frame/support/src/traits/misc.rs index c7d60234c1a6a..2ceb991c4c165 100644 --- a/frame/support/src/traits/misc.rs +++ b/frame/support/src/traits/misc.rs @@ -377,9 +377,11 @@ impl DefensiveSatura self.checked_mul(&other).defensive_unwrap_or_else(|| self.saturating_mul(other)) } fn defensive_saturating_accrue(&mut self, other: Self) { + // Use `replace` here since `take` would require `T: Default`. *self = sp_std::mem::replace(self, One::one()).defensive_saturating_add(other); } fn defensive_saturating_reduce(&mut self, other: Self) { + // Use `replace` here since `take` would require `T: Default`. *self = sp_std::mem::replace(self, One::one()).defensive_saturating_sub(other); } fn defensive_saturating_inc(&mut self) { @@ -1011,6 +1013,92 @@ mod test { use sp_core::bounded::{BoundedSlice, BoundedVec}; use sp_std::marker::PhantomData; + #[test] + #[cfg(not(debug_assertions))] + fn defensive_saturating_accrue_works() { + let mut v = 1_u32; + v.defensive_saturating_accrue(2); + assert_eq!(v, 3); + v.defensive_saturating_accrue(u32::MAX); + assert_eq!(v, u32::MAX); + v.defensive_saturating_accrue(1); + assert_eq!(v, u32::MAX); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive")] + fn defensive_saturating_accrue_panics() { + let mut v = u32::MAX; + v.defensive_saturating_accrue(1); // defensive failure + } + + #[test] + #[cfg(not(debug_assertions))] + fn defensive_saturating_reduce_works() { + let mut v = u32::MAX; + v.defensive_saturating_reduce(3); + assert_eq!(v, u32::MAX - 3); + v.defensive_saturating_reduce(u32::MAX); + assert_eq!(v, 0); + v.defensive_saturating_reduce(1); + assert_eq!(v, 0); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive")] + fn defensive_saturating_reduce_panics() { + let mut v = 0_u32; + v.defensive_saturating_reduce(1); // defensive failure + } + + #[test] + #[cfg(not(debug_assertions))] + fn defensive_saturating_inc_works() { + let mut v = 0_u32; + for i in 1..10 { + v.defensive_saturating_inc(); + assert_eq!(v, i); + } + v += u32::MAX - 10; + v.defensive_saturating_inc(); + assert_eq!(v, u32::MAX); + v.defensive_saturating_inc(); + assert_eq!(v, u32::MAX); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive")] + fn defensive_saturating_inc_panics() { + let mut v = u32::MAX; + v.defensive_saturating_inc(); // defensive failure + } + + #[test] + #[cfg(not(debug_assertions))] + fn defensive_saturating_dec_works() { + let mut v = u32::MAX; + for i in 1..10 { + v.defensive_saturating_dec(); + assert_eq!(v, u32::MAX - i); + } + v -= u32::MAX - 10; + v.defensive_saturating_dec(); + assert_eq!(v, 0); + v.defensive_saturating_dec(); + assert_eq!(v, 0); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic(expected = "Defensive")] + fn defensive_saturating_dec_panics() { + let mut v = 0_u32; + v.defensive_saturating_dec(); // defensive failure + } + #[test] #[cfg(not(debug_assertions))] fn defensive_truncating_from_vec_defensive_works() { From 25d80d3509a84aee595791d537755e1e7761ec9f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 5 Nov 2022 21:06:07 +0100 Subject: [PATCH 21/48] fmt Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/benchmarking.rs | 12 +++++++++--- frame/nis/src/lib.rs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index c54b65aecbb69..28dff04d81a69 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,11 +20,14 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller, account}; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; -use sp_runtime::{traits::{Bounded, One, Zero}, DispatchError}; +use sp_runtime::{ + traits::{Bounded, One, Zero}, + DispatchError, +}; use sp_std::prelude::*; use crate::Pallet as Nis; @@ -42,7 +45,10 @@ fn fill_queues() -> Result<(), DispatchError> { let bids = T::MaxQueueLen::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(queues + bids)); + T::Currency::make_free_balance_be( + &caller, + T::MinBid::get() * BalanceOf::::from(queues + bids), + ); for _ in 0..bids { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index bfce90a12cac5..aac93cabb59ba 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -129,7 +129,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, ConvertBack, Saturating, Zero, Bounded}, + traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero}, TokenError, }; use sp_std::prelude::*; From 9ea66816798aadfc5ac72fe363519e4bdd35a2b8 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 6 Nov 2022 10:21:25 +0000 Subject: [PATCH 22/48] Formatting --- frame/nis/src/benchmarking.rs | 12 +++-- frame/nis/src/lib.rs | 88 +++++++++++++++++------------------ 2 files changed, 53 insertions(+), 47 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index c54b65aecbb69..28dff04d81a69 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -20,11 +20,14 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; -use frame_benchmarking::{benchmarks, whitelisted_caller, account}; +use frame_benchmarking::{account, benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; -use sp_runtime::{traits::{Bounded, One, Zero}, DispatchError}; +use sp_runtime::{ + traits::{Bounded, One, Zero}, + DispatchError, +}; use sp_std::prelude::*; use crate::Pallet as Nis; @@ -42,7 +45,10 @@ fn fill_queues() -> Result<(), DispatchError> { let bids = T::MaxQueueLen::get(); let caller: T::AccountId = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(queues + bids)); + T::Currency::make_free_balance_be( + &caller, + T::MinBid::get() * BalanceOf::::from(queues + bids), + ); for _ in 0..bids { Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index bfce90a12cac5..836877b10df32 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -112,6 +112,49 @@ where } } +pub struct NoCounterpart(sp_std::marker::PhantomData); +impl FungibleInspect for NoCounterpart { + type Balance = u32; + fn total_issuance() -> u32 { + 0 + } + fn minimum_balance() -> u32 { + 0 + } + fn balance(_who: &T) -> u32 { + 0 + } + fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { + 0 + } + fn can_deposit( + _who: &T, + _amount: u32, + _mint: bool, + ) -> frame_support::traits::tokens::DepositConsequence { + frame_support::traits::tokens::DepositConsequence::Success + } + fn can_withdraw( + _who: &T, + _amount: u32, + ) -> frame_support::traits::tokens::WithdrawConsequence { + frame_support::traits::tokens::WithdrawConsequence::Success + } +} +impl FungibleMutate for NoCounterpart { + fn mint_into(_who: &T, _amount: u32) -> DispatchResult { + Ok(()) + } + fn burn_from(_who: &T, _amount: u32) -> Result { + Ok(0) + } +} +impl Convert for NoCounterpart { + fn convert(_: Perquintill) -> u32 { + 0 + } +} + #[frame_support::pallet] pub mod pallet { pub use crate::weights::WeightInfo; @@ -129,7 +172,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; use sp_runtime::{ - traits::{AccountIdConversion, Convert, ConvertBack, Saturating, Zero, Bounded}, + traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero}, TokenError, }; use sp_std::prelude::*; @@ -148,49 +191,6 @@ pub mod pallet { type BidOf = Bid, ::AccountId>; type QueueTotalsTypeOf = BoundedVec<(u32, BalanceOf), ::QueueCount>; - pub struct NoCounterpart(sp_std::marker::PhantomData); - impl FungibleInspect for NoCounterpart { - type Balance = u32; - fn total_issuance() -> u32 { - 0 - } - fn minimum_balance() -> u32 { - 0 - } - fn balance(_who: &T) -> u32 { - 0 - } - fn reducible_balance(_who: &T, _keep_alive: bool) -> u32 { - 0 - } - fn can_deposit( - _who: &T, - _amount: u32, - _mint: bool, - ) -> frame_support::traits::tokens::DepositConsequence { - frame_support::traits::tokens::DepositConsequence::Success - } - fn can_withdraw( - _who: &T, - _amount: u32, - ) -> frame_support::traits::tokens::WithdrawConsequence { - frame_support::traits::tokens::WithdrawConsequence::Success - } - } - impl FungibleMutate for NoCounterpart { - fn mint_into(_who: &T, _amount: u32) -> DispatchResult { - Ok(()) - } - fn burn_from(_who: &T, _amount: u32) -> Result { - Ok(0) - } - } - impl Convert for NoCounterpart { - fn convert(_: Perquintill) -> u32 { - 0 - } - } - #[pallet::config] pub trait Config: frame_system::Config { /// Information on runtime weights. From be66334398bca68076aa94c45acfab910b297084 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sun, 6 Nov 2022 10:23:22 +0000 Subject: [PATCH 23/48] Update frame/nis/src/lib.rs Co-authored-by: Oliver Tale-Yazdi --- frame/nis/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index aac93cabb59ba..55fa2fc43d8a9 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -479,7 +479,7 @@ pub mod pallet { fn on_initialize(n: T::BlockNumber) -> Weight { let mut weight_counter = WeightCounter { used: Weight::zero(), limit: T::MaxIntakeWeight::get() }; - if (n % T::IntakePeriod::get()).is_zero() { + if T::IntakePeriod::get().is_zero() || (n % T::IntakePeriod::get()).is_zero() { if weight_counter.check_accrue(T::WeightInfo::process_queues()) { Self::process_queues( T::Target::get(), From 471c83b5a548b68b17fd9b69a5f54af054bb24a0 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Sun, 6 Nov 2022 10:25:01 +0000 Subject: [PATCH 24/48] Apply suggestions from code review Co-authored-by: Oliver Tale-Yazdi --- frame/nis/src/lib.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 55fa2fc43d8a9..b10cc8b9fe867 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -250,6 +250,8 @@ pub mod pallet { type QueueCount: Get; /// Maximum number of items that may be in each duration queue. + /// + /// Must be larger than zero. #[pallet::constant] type MaxQueueLen: Get; @@ -549,7 +551,7 @@ pub mod pallet { QueueTotals::::mutate(|qs| { qs.bounded_resize(queue_count, (0, Zero::zero())); qs[queue_index].0 += net.0; - qs[queue_index].1 = qs[queue_index].1.saturating_add(net.1); + qs[queue_index].1.saturating_accrue(net.1); }); Self::deposit_event(Event::BidPlaced { who, amount, duration }); @@ -563,12 +565,12 @@ pub mod pallet { /// /// - `amount`: The amount of the previous bid. /// - `duration`: The duration of the previous bid. - #[pallet::weight(T::WeightInfo::place_bid(T::MaxQueueLen::get()))] + #[pallet::weight(T::WeightInfo::retract_bid(T::MaxQueueLen::get()))] pub fn retract_bid( origin: OriginFor, #[pallet::compact] amount: BalanceOf, duration: u32, - ) -> DispatchResultWithPostInfo { + ) -> DispatchResult { let who = ensure_signed(origin)?; let queue_count = T::QueueCount::get() as usize; @@ -585,13 +587,13 @@ pub mod pallet { QueueTotals::::mutate(|qs| { qs.bounded_resize(queue_count, (0, Zero::zero())); qs[queue_index].0 = new_len; - qs[queue_index].1 = qs[queue_index].1.saturating_sub(bid.amount); + qs[queue_index].1.saturating_reduce(bid.amount); }); let _ = T::Currency::unreserve(&bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); - Ok(().into()) + Ok(()) } /// Ensure we have sufficient funding for all potential payouts. From a44dc7561a72845793d80c330533963d60578ccb Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 6 Nov 2022 10:33:13 +0000 Subject: [PATCH 25/48] Events added --- frame/nis/src/lib.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index b86c6f7401070..0acbe4dd8cdd9 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -76,6 +76,10 @@ #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::{ + dispatch::{DispatchError, DispatchResult}, + traits::fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate}, +}; pub use pallet::*; use sp_arithmetic::{traits::Unsigned, RationalArg}; use sp_core::TypedGet; @@ -157,11 +161,11 @@ impl Convert for NoCounterpart { #[frame_support::pallet] pub mod pallet { + use super::{FungibleInspect, FungibleMutate}; pub use crate::weights::WeightInfo; use frame_support::{ pallet_prelude::*, traits::{ - fungible::{Inspect as FungibleInspect, Mutate as FungibleMutate}, nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, Currency, Defensive, DefensiveSaturating, ExistenceRequirement::AllowDeath, @@ -395,6 +399,8 @@ pub mod pallet { BidPlaced { who: T::AccountId, amount: BalanceOf, duration: u32 }, /// A bid was successfully removed (before being accepted). BidRetracted { who: T::AccountId, amount: BalanceOf, duration: u32 }, + /// A bid was dropped from a queue because of another, more substantial, bid was present. + BidDropped { who: T::AccountId, amount: BalanceOf, duration: u32 }, /// A bid was accepted. The balance may not be released until expiry. Issued { /// The identity of the receipt. @@ -421,6 +427,8 @@ pub mod pallet { /// If `true` then the receipt is done. dropped: bool, }, + /// An automatic funding of the deficit was made. + Funded { deficit: BalanceOf }, } #[pallet::error] @@ -534,6 +542,11 @@ pub mod pallet { let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); let _ = T::Currency::unreserve(&bid.who, bid.amount); + Self::deposit_event(Event::::BidDropped { + who: bid.who, + amount: bid.amount, + duration, + }); (0, amount - bid.amount) } else { q.try_insert(0, bid).expect("verified queue was not full above. qed."); @@ -608,6 +621,7 @@ pub mod pallet { let deficit = issuance.required.saturating_sub(issuance.holdings); ensure!(!deficit.is_zero(), Error::::Funded); T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit)); + Self::deposit_event(Event::::Funded { deficit }); Ok(()) } From f89b093769d4bd4da7029098bbc8cd9ffe6c2c0c Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 6 Nov 2022 12:06:15 +0000 Subject: [PATCH 26/48] Fix kitchensink --- bin/node/runtime/src/lib.rs | 4 ++-- frame/nis/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ecda55720e68d..d4d621cf34745 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1454,7 +1454,7 @@ parameter_types! { pub const MinBid: Balance = 100 * DOLLARS; pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const IntakePeriod: BlockNumber = 10; - pub const MaxIntakeBids: u32 = 10; + pub MaxIntakeWeight: Weight = MAXIMUM_BLOCK_WEIGHT / 10; pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); @@ -1479,7 +1479,7 @@ impl pallet_nis::Config for Runtime { type MinBid = MinBid; type MinReceipt = MinReceipt; type IntakePeriod = IntakePeriod; - type MaxIntakeBids = MaxIntakeBids; + type MaxIntakeWeight = MaxIntakeWeight; type ThawThrottle = ThawThrottle; } diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 0acbe4dd8cdd9..855dae6a2b714 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -467,7 +467,8 @@ pub mod pallet { pub(crate) limit: Weight, } impl WeightCounter { - pub fn unlimited() -> Self { + #[allow(dead_code)] + pub(crate) fn unlimited() -> Self { WeightCounter { used: Weight::zero(), limit: Weight::max_value() } } fn check_accrue(&mut self, w: Weight) -> bool { From f5a3e3d67b761a9074ecc7f76dd84cd28db4bb34 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 8 Nov 2022 18:19:57 +0000 Subject: [PATCH 27/48] Update frame/nis/src/lib.rs Co-authored-by: Xiliang Chen --- frame/nis/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 855dae6a2b714..2b3c926dee2ee 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -604,7 +604,7 @@ pub mod pallet { qs[queue_index].1.saturating_reduce(bid.amount); }); - let _ = T::Currency::unreserve(&bid.who, bid.amount); + let _ = T::Currency::unreserve(&bid.who, bid.amount).defensive(); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(()) From 1474a753c0dd18c3a963453f94c80eb792d3bba3 Mon Sep 17 00:00:00 2001 From: Gav Date: Thu, 10 Nov 2022 16:33:25 +0000 Subject: [PATCH 28/48] Review niggles --- frame/nis/src/lib.rs | 6 +- .../src/traits/tokens/currency/reservable.rs | 114 +++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 855dae6a2b714..5ae28055b6956 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -429,6 +429,8 @@ pub mod pallet { }, /// An automatic funding of the deficit was made. Funded { deficit: BalanceOf }, + /// A receipt was transfered. + Transferred { from: T::AccountId, to: T::AccountId, index: ReceiptIndex }, } #[pallet::error] @@ -735,8 +737,10 @@ pub mod pallet { impl NonfungibleTransfer for Pallet { fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult { let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; + let from = item.who; item.who = destination.clone(); - Receipts::::insert(index, item); + Receipts::::insert(&index, &item); + Pallet::::deposit_event(Event::::Transferred { from, to: item.who, index: *index }); Ok(()) } } diff --git a/frame/support/src/traits/tokens/currency/reservable.rs b/frame/support/src/traits/tokens/currency/reservable.rs index 35455aaecdb49..1e1c898fed896 100644 --- a/frame/support/src/traits/tokens/currency/reservable.rs +++ b/frame/support/src/traits/tokens/currency/reservable.rs @@ -17,8 +17,11 @@ //! The reservable currency trait. +use scale_info::TypeInfo; +use sp_core::Get; + use super::{super::misc::BalanceStatus, Currency}; -use crate::dispatch::{DispatchError, DispatchResult}; +use crate::{dispatch::{DispatchError, DispatchResult}, traits::{WithdrawReasons, ExistenceRequirement, SignedImbalance}}; /// A currency where funds can be reserved from the user. pub trait ReservableCurrency: Currency { @@ -111,7 +114,7 @@ impl ReservableCurrency for () { pub trait NamedReservableCurrency: ReservableCurrency { /// An identifier for a reserve. Used for disambiguating different reserves so that /// they can be individually replaced or removed. - type ReserveIdentifier; + type ReserveIdentifier: codec::Encode + TypeInfo + 'static; /// Deducts up to `value` from reserved balance of `who`. This function cannot fail. /// @@ -236,3 +239,110 @@ pub trait NamedReservableCurrency: ReservableCurrency { Self::repatriate_reserved_named(id, slashed, beneficiary, value, status).map(|_| ()) } } + +/// Adapter to allow a `NamedReservableCurrency` to be passed as regular `ReservableCurrency` +/// together with an `Id`. +/// +/// All "anonymous" operations are then implemented as their named counterparts with the given `Id`. +pub struct WithName( + sp_std::marker::PhantomData<(NamedReservable, Id, AccountId)> +); +impl< + NamedReservable: NamedReservableCurrency, + Id: Get, + AccountId, +> Currency for WithName { + type Balance = >::Balance; + type PositiveImbalance = >::PositiveImbalance; + type NegativeImbalance = >::NegativeImbalance; + + fn total_balance(who: &AccountId) -> Self::Balance { NamedReservable::total_balance(who) } + fn can_slash(who: &AccountId, value: Self::Balance) -> bool { NamedReservable::can_slash(who, value) } + fn total_issuance() -> Self::Balance { NamedReservable::total_issuance() } + fn minimum_balance() -> Self::Balance { NamedReservable::minimum_balance() } + fn burn(amount: Self::Balance) -> Self::PositiveImbalance { NamedReservable::burn(amount) } + fn issue(amount: Self::Balance) -> Self::NegativeImbalance { NamedReservable::issue(amount) } + fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) { + NamedReservable::pair(amount) + } + fn free_balance(who: &AccountId) -> Self::Balance { NamedReservable::free_balance(who) } + fn ensure_can_withdraw( + who: &AccountId, + amount: Self::Balance, + reasons: WithdrawReasons, + new_balance: Self::Balance, + ) -> DispatchResult { NamedReservable::ensure_can_withdraw(who, amount, reasons, new_balance) } + + fn transfer( + source: &AccountId, + dest: &AccountId, + value: Self::Balance, + existence_requirement: ExistenceRequirement, + ) -> DispatchResult { NamedReservable::transfer(source, dest, value, existence_requirement) } + fn slash(who: &AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { NamedReservable::slash(who, value) } + fn deposit_into_existing( + who: &AccountId, + value: Self::Balance, + ) -> Result { NamedReservable::deposit_into_existing(who, value) } + fn resolve_into_existing( + who: &AccountId, + value: Self::NegativeImbalance, + ) -> Result<(), Self::NegativeImbalance> { NamedReservable::resolve_into_existing(who, value) } + fn deposit_creating(who: &AccountId, value: Self::Balance) -> Self::PositiveImbalance { NamedReservable::deposit_creating(who, value) } + fn resolve_creating(who: &AccountId, value: Self::NegativeImbalance) { NamedReservable::resolve_creating(who, value) } + fn withdraw( + who: &AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> Result { + NamedReservable::withdraw(who, value, reasons, liveness) + } + fn settle( + who: &AccountId, + value: Self::PositiveImbalance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> Result<(), Self::PositiveImbalance> { NamedReservable::settle(who, value, reasons, liveness) } + fn make_free_balance_be( + who: &AccountId, + balance: Self::Balance, + ) -> SignedImbalance { NamedReservable::make_free_balance_be(who, balance) } +} +impl< + NamedReservable: NamedReservableCurrency, + Id: Get, + AccountId, +> ReservableCurrency for WithName { + fn can_reserve(who: &AccountId, value: Self::Balance) -> bool { + NamedReservable::can_reserve(who, value) + } + + fn slash_reserved( + who: &AccountId, + value: Self::Balance, + ) -> (Self::NegativeImbalance, Self::Balance) { + NamedReservable::slash_reserved_named(&Id::get(), who, value) + } + + fn reserved_balance(who: &AccountId) -> Self::Balance { + NamedReservable::reserved_balance_named(&Id::get(), who) + } + + fn reserve(who: &AccountId, value: Self::Balance) -> DispatchResult { + NamedReservable::reserve_named(&Id::get(), who, value) + } + + fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance { + NamedReservable::unreserve_named(&Id::get(), who, value) + } + + fn repatriate_reserved( + slashed: &AccountId, + beneficiary: &AccountId, + value: Self::Balance, + status: BalanceStatus, + ) -> Result { + NamedReservable::repatriate_reserved_named(&Id::get(), slashed, beneficiary, value, status) + } +} From ef9a2397b0d48079212dcbcc73eb43f083da7a53 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 11 Nov 2022 13:10:55 +0100 Subject: [PATCH 29/48] Remove genesis build requirements --- frame/nis/src/lib.rs | 28 ++---- frame/nis/src/mock.rs | 6 +- frame/nis/src/tests.rs | 1 - .../src/traits/tokens/currency/reservable.rs | 89 +++++++++++++------ 4 files changed, 73 insertions(+), 51 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 352995f427e04..dea2c581d9ede 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -377,21 +377,6 @@ pub mod pallet { pub type Receipts = StorageMap<_, Blake2_128Concat, ReceiptIndex, ReceiptRecordOf, OptionQuery>; - #[pallet::genesis_config] - #[derive(Default)] - pub struct GenesisConfig; - - #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { - fn build(&self) { - let unbounded = vec![(0, BalanceOf::::zero()); T::QueueCount::get() as usize]; - let bounded: BoundedVec<_, _> = unbounded - .try_into() - .expect("QueueTotals should support up to QueueCount items. qed"); - QueueTotals::::put(bounded); - } - } - #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { @@ -606,7 +591,7 @@ pub mod pallet { qs[queue_index].1.saturating_reduce(bid.amount); }); - let _ = T::Currency::unreserve(&bid.who, bid.amount).defensive(); + T::Currency::unreserve(&bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(()) @@ -740,7 +725,11 @@ pub mod pallet { let from = item.who; item.who = destination.clone(); Receipts::::insert(&index, &item); - Pallet::::deposit_event(Event::::Transferred { from, to: item.who, index: *index }); + Pallet::::deposit_event(Event::::Transferred { + from, + to: item.who, + index: *index, + }); Ok(()) } } @@ -805,8 +794,9 @@ pub mod pallet { let mut queues_hit = 0; let mut bids_hit = 0; let mut totals = QueueTotals::::get(); - let count = T::QueueCount::get(); - for duration in (1..=count).rev() { + let queue_count = T::QueueCount::get(); + totals.bounded_resize(queue_count as usize, (0, Zero::zero())); + for duration in (1..=queue_count).rev() { if totals[duration as usize - 1].0.is_zero() { continue } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index e7a34cec103e8..478b8e67b334b 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -21,10 +21,7 @@ use crate::{self as pallet_nis, Perquintill, WithMaximumOf}; use frame_support::{ ord_parameter_types, parameter_types, - traits::{ - ConstU16, ConstU32, ConstU64, Currency, GenesisBuild, OnFinalize, OnInitialize, - StorageMapShim, - }, + traits::{ConstU16, ConstU32, ConstU64, Currency, OnFinalize, OnInitialize, StorageMapShim}, weights::Weight, PalletId, }; @@ -153,7 +150,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } .assimilate_storage(&mut t) .unwrap(); - GenesisBuild::::assimilate_storage(&crate::GenesisConfig, &mut t).unwrap(); t.into() } diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index da5c56af46788..f0c45cc80b0e5 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -58,7 +58,6 @@ fn basic_setup_works() { thawed: Perquintill::zero() } ); - assert_eq!(QueueTotals::::get(), vec![(0, 0); 3]); }); } diff --git a/frame/support/src/traits/tokens/currency/reservable.rs b/frame/support/src/traits/tokens/currency/reservable.rs index 1e1c898fed896..53f6764c3a1ac 100644 --- a/frame/support/src/traits/tokens/currency/reservable.rs +++ b/frame/support/src/traits/tokens/currency/reservable.rs @@ -21,7 +21,10 @@ use scale_info::TypeInfo; use sp_core::Get; use super::{super::misc::BalanceStatus, Currency}; -use crate::{dispatch::{DispatchError, DispatchResult}, traits::{WithdrawReasons, ExistenceRequirement, SignedImbalance}}; +use crate::{ + dispatch::{DispatchError, DispatchResult}, + traits::{ExistenceRequirement, SignedImbalance, WithdrawReasons}, +}; /// A currency where funds can be reserved from the user. pub trait ReservableCurrency: Currency { @@ -245,51 +248,80 @@ pub trait NamedReservableCurrency: ReservableCurrency { /// /// All "anonymous" operations are then implemented as their named counterparts with the given `Id`. pub struct WithName( - sp_std::marker::PhantomData<(NamedReservable, Id, AccountId)> + sp_std::marker::PhantomData<(NamedReservable, Id, AccountId)>, ); impl< - NamedReservable: NamedReservableCurrency, - Id: Get, - AccountId, -> Currency for WithName { + NamedReservable: NamedReservableCurrency, + Id: Get, + AccountId, + > Currency for WithName +{ type Balance = >::Balance; type PositiveImbalance = >::PositiveImbalance; type NegativeImbalance = >::NegativeImbalance; - fn total_balance(who: &AccountId) -> Self::Balance { NamedReservable::total_balance(who) } - fn can_slash(who: &AccountId, value: Self::Balance) -> bool { NamedReservable::can_slash(who, value) } - fn total_issuance() -> Self::Balance { NamedReservable::total_issuance() } - fn minimum_balance() -> Self::Balance { NamedReservable::minimum_balance() } - fn burn(amount: Self::Balance) -> Self::PositiveImbalance { NamedReservable::burn(amount) } - fn issue(amount: Self::Balance) -> Self::NegativeImbalance { NamedReservable::issue(amount) } + fn total_balance(who: &AccountId) -> Self::Balance { + NamedReservable::total_balance(who) + } + fn can_slash(who: &AccountId, value: Self::Balance) -> bool { + NamedReservable::can_slash(who, value) + } + fn total_issuance() -> Self::Balance { + NamedReservable::total_issuance() + } + fn minimum_balance() -> Self::Balance { + NamedReservable::minimum_balance() + } + fn burn(amount: Self::Balance) -> Self::PositiveImbalance { + NamedReservable::burn(amount) + } + fn issue(amount: Self::Balance) -> Self::NegativeImbalance { + NamedReservable::issue(amount) + } fn pair(amount: Self::Balance) -> (Self::PositiveImbalance, Self::NegativeImbalance) { NamedReservable::pair(amount) } - fn free_balance(who: &AccountId) -> Self::Balance { NamedReservable::free_balance(who) } + fn free_balance(who: &AccountId) -> Self::Balance { + NamedReservable::free_balance(who) + } fn ensure_can_withdraw( who: &AccountId, amount: Self::Balance, reasons: WithdrawReasons, new_balance: Self::Balance, - ) -> DispatchResult { NamedReservable::ensure_can_withdraw(who, amount, reasons, new_balance) } + ) -> DispatchResult { + NamedReservable::ensure_can_withdraw(who, amount, reasons, new_balance) + } fn transfer( source: &AccountId, dest: &AccountId, value: Self::Balance, existence_requirement: ExistenceRequirement, - ) -> DispatchResult { NamedReservable::transfer(source, dest, value, existence_requirement) } - fn slash(who: &AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { NamedReservable::slash(who, value) } + ) -> DispatchResult { + NamedReservable::transfer(source, dest, value, existence_requirement) + } + fn slash(who: &AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { + NamedReservable::slash(who, value) + } fn deposit_into_existing( who: &AccountId, value: Self::Balance, - ) -> Result { NamedReservable::deposit_into_existing(who, value) } + ) -> Result { + NamedReservable::deposit_into_existing(who, value) + } fn resolve_into_existing( who: &AccountId, value: Self::NegativeImbalance, - ) -> Result<(), Self::NegativeImbalance> { NamedReservable::resolve_into_existing(who, value) } - fn deposit_creating(who: &AccountId, value: Self::Balance) -> Self::PositiveImbalance { NamedReservable::deposit_creating(who, value) } - fn resolve_creating(who: &AccountId, value: Self::NegativeImbalance) { NamedReservable::resolve_creating(who, value) } + ) -> Result<(), Self::NegativeImbalance> { + NamedReservable::resolve_into_existing(who, value) + } + fn deposit_creating(who: &AccountId, value: Self::Balance) -> Self::PositiveImbalance { + NamedReservable::deposit_creating(who, value) + } + fn resolve_creating(who: &AccountId, value: Self::NegativeImbalance) { + NamedReservable::resolve_creating(who, value) + } fn withdraw( who: &AccountId, value: Self::Balance, @@ -303,17 +335,22 @@ impl< value: Self::PositiveImbalance, reasons: WithdrawReasons, liveness: ExistenceRequirement, - ) -> Result<(), Self::PositiveImbalance> { NamedReservable::settle(who, value, reasons, liveness) } + ) -> Result<(), Self::PositiveImbalance> { + NamedReservable::settle(who, value, reasons, liveness) + } fn make_free_balance_be( who: &AccountId, balance: Self::Balance, - ) -> SignedImbalance { NamedReservable::make_free_balance_be(who, balance) } + ) -> SignedImbalance { + NamedReservable::make_free_balance_be(who, balance) + } } impl< - NamedReservable: NamedReservableCurrency, - Id: Get, - AccountId, -> ReservableCurrency for WithName { + NamedReservable: NamedReservableCurrency, + Id: Get, + AccountId, + > ReservableCurrency for WithName +{ fn can_reserve(who: &AccountId, value: Self::Balance) -> bool { NamedReservable::can_reserve(who, value) } From 207a4b5b90be892b34e3e2601f83d980cde128a6 Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 14 Nov 2022 14:05:21 +0100 Subject: [PATCH 30/48] Grumbles --- bin/node/cli/src/chain_spec.rs | 1 - bin/node/testing/src/genesis.rs | 1 - frame/nis/src/lib.rs | 13 +++++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 3a648d378c977..7e710183d4e1c 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -359,7 +359,6 @@ pub fn testnet_genesis( }, vesting: Default::default(), assets: Default::default(), - nis: Default::default(), transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index fe00df6135bfa..a3fec4b3f22be 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -89,7 +89,6 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), assets: Default::default(), - nis: Default::default(), transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index dea2c581d9ede..83220f7c3374e 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -355,13 +355,21 @@ pub mod pallet { pub last_period: BlockNumber, } + pub struct OnEmptyQueueTotals(sp_std::marker::PhantomData); + impl Get> for OnEmptyQueueTotals { + fn get() -> QueueTotalsTypeOf { + frame_support::bounded_vec![(0, Zero::zero()); ::QueueCount::get() as usize] + } + } + /// The totals of items and balances within each queue. Saves a lot of storage reads in the /// case of sparsely packed queues. /// /// The vector is indexed by duration in `Period`s, offset by one, so information on the queue /// whose duration is one `Period` would be storage `0`. #[pallet::storage] - pub type QueueTotals = StorageValue<_, QueueTotalsTypeOf, ValueQuery>; + pub type QueueTotals = + StorageValue<_, QueueTotalsTypeOf, ValueQuery, OnEmptyQueueTotals>; /// The queues of bids. Indexed by duration (in `Period`s). #[pallet::storage] @@ -521,7 +529,6 @@ pub mod pallet { duration, |q| -> Result<(u32, BalanceOf), DispatchError> { let queue_full = q.len() == T::MaxQueueLen::get() as usize; - // ensure!(have_reserved(q[0].amount)); ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); T::Currency::reserve(&who, amount)?; @@ -861,6 +868,8 @@ pub mod pallet { summary, ) { queue.try_push(bid).expect("just popped, so there must be space. qed"); + // This should exit at the next iteration (though nothing will break if it + // doesn't). } count.saturating_inc(); } From 6a8501271c4487762eccd00fee691044389b8aad Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 14 Nov 2022 14:11:43 +0100 Subject: [PATCH 31/48] Fixes --- Cargo.lock | 2 +- frame/nis/src/lib.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1b948db2c5348..3eaac61f43e95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6329,8 +6329,8 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", - "pallet-root-testing", "pallet-collective", + "pallet-root-testing", "pallet-timestamp", "parity-scale-codec", "scale-info", diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 83220f7c3374e..abad524b17c52 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -164,6 +164,7 @@ pub mod pallet { use super::{FungibleInspect, FungibleMutate}; pub use crate::weights::WeightInfo; use frame_support::{ + bounded_vec, pallet_prelude::*, traits::{ nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, @@ -358,7 +359,7 @@ pub mod pallet { pub struct OnEmptyQueueTotals(sp_std::marker::PhantomData); impl Get> for OnEmptyQueueTotals { fn get() -> QueueTotalsTypeOf { - frame_support::bounded_vec![(0, Zero::zero()); ::QueueCount::get() as usize] + bounded_vec![(0, Zero::zero()); ::QueueCount::get() as usize] } } From d11f04edd9cc3d9d67d08a7ae6ff1a713d54a93b Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 14 Nov 2022 14:22:01 +0100 Subject: [PATCH 32/48] Fixes --- frame/nis/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index abad524b17c52..b0db32dd9a2f6 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -164,7 +164,6 @@ pub mod pallet { use super::{FungibleInspect, FungibleMutate}; pub use crate::weights::WeightInfo; use frame_support::{ - bounded_vec, pallet_prelude::*, traits::{ nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, @@ -176,6 +175,7 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; + use sp_core::bounded_vec; use sp_runtime::{ traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero}, TokenError, From 8b47006e79a97ee5a012fe6413f2988ddf231c4d Mon Sep 17 00:00:00 2001 From: Gav Date: Mon, 14 Nov 2022 15:19:48 +0100 Subject: [PATCH 33/48] Fixes --- frame/nis/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index b0db32dd9a2f6..93c233301df66 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -175,7 +175,6 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use sp_arithmetic::{PerThing, Perquintill}; - use sp_core::bounded_vec; use sp_runtime::{ traits::{AccountIdConversion, Bounded, Convert, ConvertBack, Saturating, Zero}, TokenError, @@ -359,7 +358,10 @@ pub mod pallet { pub struct OnEmptyQueueTotals(sp_std::marker::PhantomData); impl Get> for OnEmptyQueueTotals { fn get() -> QueueTotalsTypeOf { - bounded_vec![(0, Zero::zero()); ::QueueCount::get() as usize] + BoundedVec::truncate_from(vec![ + (0, Zero::zero()); + ::QueueCount::get() as usize + ]) } } From 2a59d93009b666e002adacedb5135671d8a7e801 Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 22 Nov 2022 04:53:22 +0000 Subject: [PATCH 34/48] Update frame/nis/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- frame/nis/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 93c233301df66..fe45be8fb2d9a 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -500,6 +500,11 @@ pub mod pallet { } weight_counter.used } + + fn integrity_test() { + assert!(!T::IntakePeriod::get().is_zero()); + assert!(!T::MaxQueueLen::get().is_zero()); + } } #[pallet::call] From cd45d8ae650ae218ed366351c3a19f37b62d820f Mon Sep 17 00:00:00 2001 From: Gavin Wood Date: Tue, 22 Nov 2022 05:03:15 +0000 Subject: [PATCH 35/48] Update primitives/runtime/src/traits.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> --- primitives/runtime/src/traits.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/primitives/runtime/src/traits.rs b/primitives/runtime/src/traits.rs index 452cb44736a65..276a62349a175 100644 --- a/primitives/runtime/src/traits.rs +++ b/primitives/runtime/src/traits.rs @@ -532,7 +532,7 @@ impl> Convert for ConvertInto { /// Extensible conversion trait. Generic over both source and destination types. pub trait ConvertBack: Convert { /// Make conversion back. - fn convert_back(a: B) -> A; + fn convert_back(b: B) -> A; } /// Convenience type to work around the highly unergonomic syntax needed From 990f2807ae8d3acc52f670a2326efa7b7381a4cf Mon Sep 17 00:00:00 2001 From: Gav Date: Tue, 22 Nov 2022 23:25:38 +0900 Subject: [PATCH 36/48] Formatting --- frame/nis/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index fe45be8fb2d9a..8ee47e0c4ab31 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -500,7 +500,7 @@ pub mod pallet { } weight_counter.used } - + fn integrity_test() { assert!(!T::IntakePeriod::get().is_zero()); assert!(!T::MaxQueueLen::get().is_zero()); From 15bd0acad992d10c052ee25cbcc55c355bddf39f Mon Sep 17 00:00:00 2001 From: Gav Date: Wed, 23 Nov 2022 15:51:18 +0900 Subject: [PATCH 37/48] Fixes --- Cargo.lock | 1 + bin/node/testing/Cargo.toml | 1 + bin/node/testing/src/genesis.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 5d7439e5ea3cc..4a2c427480446 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4781,6 +4781,7 @@ dependencies = [ "node-executor", "node-primitives", "pallet-asset-tx-payment", + "pallet-assets", "pallet-transaction-payment", "parity-scale-codec", "sc-block-builder", diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index a2b34cf59b120..694472123647a 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -22,6 +22,7 @@ frame-system = { version = "4.0.0-dev", path = "../../../frame/system" } node-executor = { version = "3.0.0-dev", path = "../executor" } node-primitives = { version = "2.0.0", path = "../primitives" } kitchensink-runtime = { version = "3.0.0-dev", path = "../runtime" } +pallet-assets = { version = "4.0.0-dev", path = "../../../frame/assets" } pallet-asset-tx-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment/asset-tx-payment" } pallet-transaction-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment" } sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index a3fec4b3f22be..0d356be64fd46 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -88,7 +88,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen treasury: Default::default(), society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), - assets: Default::default(), + assets: pallet_assets::Config { assets: vec![(0, alice(), true, 0)], ..Default::default() }, transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), From 9578bf00d9c43898dda14168dec5f6a6e78f75f5 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 14:31:34 +0100 Subject: [PATCH 38/48] Fix node genesis config Signed-off-by: Oliver Tale-Yazdi --- bin/node/testing/src/genesis.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 0d356be64fd46..51d824fe20bd6 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -88,7 +88,10 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen treasury: Default::default(), society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), - assets: pallet_assets::Config { assets: vec![(0, alice(), true, 0)], ..Default::default() }, + assets: pallet_assets::GenesisConfig { + assets: vec![(0, alice(), true, 0)], + ..Default::default() + }, transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), From ecf4bc1a9aa3627d3296960bfe8772346262d120 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 15:46:20 +0100 Subject: [PATCH 39/48] Fix node chain specs Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + bin/node/cli/Cargo.toml | 1 + bin/node/cli/src/chain_spec.rs | 6 +++++- bin/node/testing/src/genesis.rs | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a2c427480446..92b922efd2c2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4525,6 +4525,7 @@ dependencies = [ "node-primitives", "node-rpc", "pallet-asset-tx-payment", + "pallet-assets", "pallet-balances", "pallet-im-online", "pallet-timestamp", diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index d56764f9e2040..c3bb314aabc3b 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -84,6 +84,7 @@ sc-sysinfo = { version = "6.0.0-dev", path = "../../../client/sysinfo" } frame-system = { version = "4.0.0-dev", path = "../../../frame/system" } frame-system-rpc-runtime-api = { version = "4.0.0-dev", path = "../../../frame/system/rpc/runtime-api" } pallet-transaction-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment" } +pallet-assets = { version = "4.0.0-dev", path = "../../../frame/assets/" } pallet-asset-tx-payment = { version = "4.0.0-dev", path = "../../../frame/transaction-payment/asset-tx-payment/" } pallet-im-online = { version = "4.0.0-dev", default-features = false, path = "../../../frame/im-online" } diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 7e710183d4e1c..56be42ef5330b 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -358,7 +358,11 @@ pub fn testnet_genesis( max_members: 999, }, vesting: Default::default(), - assets: Default::default(), + assets: pallet_assets::GenesisConfig { + // This asset is used by the NIS pallet as counterpart currency. + assets: vec![(0, get_account_id_from_seed::("Alice"), true, 1)], + ..Default::default() + }, transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 51d824fe20bd6..2a628e7fcb5e4 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -89,7 +89,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), assets: pallet_assets::GenesisConfig { - assets: vec![(0, alice(), true, 0)], + assets: vec![(0, alice(), true, 1)], ..Default::default() }, transaction_storage: Default::default(), From 0a57aed2bd541f78721cf54b5811b7f4131e21ce Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 16:08:32 +0100 Subject: [PATCH 40/48] Use free asset ID as counterpart Signed-off-by: Oliver Tale-Yazdi --- bin/node/cli/src/chain_spec.rs | 2 +- bin/node/runtime/src/lib.rs | 2 +- bin/node/testing/src/genesis.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 56be42ef5330b..1e4e806fd2736 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -360,7 +360,7 @@ pub fn testnet_genesis( vesting: Default::default(), assets: pallet_assets::GenesisConfig { // This asset is used by the NIS pallet as counterpart currency. - assets: vec![(0, get_account_id_from_seed::("Alice"), true, 1)], + assets: vec![(9, get_account_id_from_seed::("Alice"), true, 1)], ..Default::default() }, transaction_storage: Default::default(), diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4cafef46363fe..1345310f8915f 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1477,7 +1477,7 @@ impl pallet_nis::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type FundOrigin = frame_system::EnsureSigned; - type Counterpart = ItemOf, AccountId>; + type Counterpart = ItemOf, AccountId>; type CounterpartAmount = WithMaximumOf>; type Deficit = (); type IgnoredIssuance = IgnoredIssuance; diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 2a628e7fcb5e4..ef6626b1548d8 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -89,7 +89,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), assets: pallet_assets::GenesisConfig { - assets: vec![(0, alice(), true, 1)], + assets: vec![(9, alice(), true, 1)], ..Default::default() }, transaction_storage: Default::default(), From 6e13e91adb795a2872a7f46c12c8cce1a452aa63 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 16:08:55 +0100 Subject: [PATCH 41/48] Account for rounding errors in fund_deficit bench Relaxes the check for the NIS account balance in the fund_deficit bench from equality from to checking for 99.999% equality. The exact deviation for the kitchensink runtime config is 1.24e-10 percent but could vary if the config is changed. Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/benchmarking.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 28dff04d81a69..606b1c484b1e8 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -26,7 +26,7 @@ use frame_system::RawOrigin; use sp_arithmetic::Perquintill; use sp_runtime::{ traits::{Bounded, One, Zero}, - DispatchError, + DispatchError, PerThing, }; use sp_std::prelude::*; @@ -110,7 +110,10 @@ benchmarks! { T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) verify { - assert_eq!(original, T::Currency::free_balance(&Nis::::account_id())); + // Must fund at least 99.999% of the required amount. + let missing = Perquintill::from_rational( + T::Currency::free_balance(&Nis::::account_id()), original).left_from_one(); + assert!(missing <= Perquintill::one() / 100_000); } thaw { From 45c58b421a0f2f29cd8c80c962e198abfd4f4247 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 16:18:57 +0100 Subject: [PATCH 42/48] clippy Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 8ee47e0c4ab31..2777bda9064c1 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -927,7 +927,7 @@ pub mod pallet { expiry, who: who.clone(), amount, - proportion: proportion.clone(), + proportion, }; Self::deposit_event(e); let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; From e4499b4b960e5ac670aa782e4a3c34519061843e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 23 Nov 2022 16:22:11 +0100 Subject: [PATCH 43/48] fmt Signed-off-by: Oliver Tale-Yazdi --- frame/nis/src/lib.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 2777bda9064c1..24cef1ca7de3a 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -922,13 +922,7 @@ pub mod pallet { summary.proportion_owed.defensive_saturating_accrue(proportion); summary.index += 1; - let e = Event::Issued { - index, - expiry, - who: who.clone(), - amount, - proportion, - }; + let e = Event::Issued { index, expiry, who: who.clone(), amount, proportion }; Self::deposit_event(e); let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; Receipts::::insert(index, receipt); From 335ca073cfbe516b2d28dde3f60c1d68ca136019 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 27 Nov 2022 13:32:46 +0000 Subject: [PATCH 44/48] Fix --- bin/node/testing/src/genesis.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 0d356be64fd46..18628811bd1f7 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -20,7 +20,7 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, AccountId, BabeConfig, BalancesConfig, + constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, }; @@ -88,7 +88,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen treasury: Default::default(), society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), - assets: pallet_assets::Config { assets: vec![(0, alice(), true, 0)], ..Default::default() }, + assets: AssetsConfig { assets: vec![(0, alice(), true, 0)], ..Default::default() }, transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), From 314728f6b40699df83aa7eff5a65a07730e8f01a Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 27 Nov 2022 16:23:07 +0000 Subject: [PATCH 45/48] Rename --- bin/node/runtime/src/lib.rs | 4 ++-- bin/node/testing/src/genesis.rs | 6 +++--- frame/nis/src/lib.rs | 4 ++-- frame/nis/src/mock.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4cafef46363fe..6ce03dd857b9e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1461,7 +1461,7 @@ parameter_types! { pub const QueueCount: u32 = 300; pub const MaxQueueLen: u32 = 1000; pub const FifoQueueLen: u32 = 500; - pub const Period: BlockNumber = 30 * DAYS; + pub const NisBasePeriod: BlockNumber = 30 * DAYS; pub const MinBid: Balance = 100 * DOLLARS; pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const IntakePeriod: BlockNumber = 10; @@ -1486,7 +1486,7 @@ impl pallet_nis::Config for Runtime { type QueueCount = QueueCount; type MaxQueueLen = MaxQueueLen; type FifoQueueLen = FifoQueueLen; - type Period = Period; + type BasePeriod = NisBasePeriod; type MinBid = MinBid; type MinReceipt = MinReceipt; type IntakePeriod = IntakePeriod; diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 18628811bd1f7..d5df99768ab91 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -20,9 +20,9 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, BalancesConfig, - GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus, - StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, + BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, + StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, }; use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use sp_runtime::Perbill; diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index 8ee47e0c4ab31..ffe42a018c24b 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -268,7 +268,7 @@ pub mod pallet { /// The base period for the duration queues. This is the common multiple across all /// supported freezing durations that can be bid upon. #[pallet::constant] - type Period: Get; + type BasePeriod: Get; /// The minimum amount of funds that may be placed in a bid. Note that this /// does not actually limit the amount which may be represented in a receipt since bids may @@ -854,7 +854,7 @@ pub mod pallet { weight: &mut WeightCounter, ) -> u32 { let mut queue: BoundedVec, _> = Queues::::get(&duration); - let expiry = now.saturating_add(T::Period::get().saturating_mul(duration.into())); + let expiry = now.saturating_add(T::BasePeriod::get().saturating_mul(duration.into())); let mut count = 0; while count < max_bids && diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index 478b8e67b334b..ebe073d683309 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -133,7 +133,7 @@ impl pallet_nis::Config for Test { type QueueCount = ConstU32<3>; type MaxQueueLen = ConstU32<3>; type FifoQueueLen = ConstU32<1>; - type Period = ConstU64<3>; + type BasePeriod = ConstU64<3>; type MinBid = ConstU64<2>; type IntakePeriod = ConstU64<2>; type MaxIntakeWeight = MaxIntakeWeight; From 52e64306076a9b27213d8c2c3f505a211772b304 Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 27 Nov 2022 21:31:20 +0000 Subject: [PATCH 46/48] Fixes --- bin/node/testing/src/genesis.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index bc1f7760a391a..ef6626b1548d8 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -20,9 +20,9 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, - BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, - StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, wasm_binary_unwrap, AccountId, BabeConfig, BalancesConfig, + GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus, + StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, }; use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use sp_runtime::Perbill; From 5817e4637f4f2236d65f40e0c738ecdf8a21e63f Mon Sep 17 00:00:00 2001 From: Gav Date: Sun, 27 Nov 2022 21:31:43 +0000 Subject: [PATCH 47/48] Fixes --- bin/node/testing/src/genesis.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index ef6626b1548d8..b207fc7f98ab4 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -20,9 +20,9 @@ use crate::keyring::*; use kitchensink_runtime::{ - constants::currency::*, wasm_binary_unwrap, AccountId, BabeConfig, BalancesConfig, - GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, StakerStatus, - StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, + constants::currency::*, wasm_binary_unwrap, AccountId, AssetsConfig, BabeConfig, + BalancesConfig, GenesisConfig, GrandpaConfig, IndicesConfig, SessionConfig, SocietyConfig, + StakerStatus, StakingConfig, SystemConfig, BABE_GENESIS_EPOCH_CONFIG, }; use sp_keyring::{Ed25519Keyring, Sr25519Keyring}; use sp_runtime::Perbill; @@ -88,10 +88,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen treasury: Default::default(), society: SocietyConfig { members: vec![alice(), bob()], pot: 0, max_members: 999 }, vesting: Default::default(), - assets: pallet_assets::GenesisConfig { - assets: vec![(9, alice(), true, 1)], - ..Default::default() - }, + assets: AssetsConfig { assets: vec![(9, alice(), true, 1)], ..Default::default() }, transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), From 2a18a4644fdf0bb718124455d21beb1295610987 Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 2 Dec 2022 14:17:57 +0100 Subject: [PATCH 48/48] Formatting --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 4c7f8ef84f8b1..633106e10b6f8 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -32,8 +32,8 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, - EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, + fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, + Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, },