From d598a1ee5f81294444b490b6f1b93bca9ea3e6a0 Mon Sep 17 00:00:00 2001 From: eskimor Date: Fri, 10 May 2024 15:52:56 +0200 Subject: [PATCH 01/33] Price adjustements should be based on price not on number of cores. I have to think a bit more about this, but I believe we will arrive have been sold at almost the base price --- substrate/frame/broker/src/adapt_price.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index fbcd7afdf0da..79f6f77bd5b1 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -21,19 +21,27 @@ use crate::CoreIndex; use sp_arithmetic::{traits::One, FixedU64}; use sp_runtime::Saturating; +struct SalePerformance { + /// The price at which the last core was sold. + /// + /// Will be `None` if no cores have been offered. + purchase_price: Option, + + /// The base price (lowest possible price) that was used in this sale. + price: Balance, +} + + /// Type for determining how to set price. -pub trait AdaptPrice { +pub trait AdaptPrice { /// Return the factor by which the regular price must be multiplied during the leadin period. /// /// - `when`: The amount through the leadin period; between zero and one. fn leadin_factor_at(when: FixedU64) -> FixedU64; - /// Return the correction factor by which the regular price must be multiplied based on market + + /// Return the base price that should be used in the next sale, based on this sale's /// performance. - /// - /// - `sold`: The number of cores sold. - /// - `target`: The target number of cores to be sold (must be larger than zero). - /// - `limit`: The maximum number of cores to be sold. - fn adapt_price(sold: CoreIndex, target: CoreIndex, limit: CoreIndex) -> FixedU64; + fn adapt_price(SalePerformance) -> Balance; } impl AdaptPrice for () { From ae9065044552f2351738b06adee141a2dbd4cf6e Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 20 May 2024 13:42:01 +0200 Subject: [PATCH 02/33] AllowedRenewal -> PotentialRenewal The old type was very misleading as an entry in `AllowedRenewals` does not mean that a renewal is actually allowed. --- .../src/weights/pallet_broker.rs | 8 ++--- .../src/weights/pallet_broker.rs | 8 ++--- substrate/frame/broker/src/benchmarking.rs | 14 ++++---- .../frame/broker/src/dispatchable_impls.rs | 32 ++++++++++--------- substrate/frame/broker/src/lib.rs | 10 +++--- substrate/frame/broker/src/tests.rs | 18 +++++------ substrate/frame/broker/src/tick_impls.rs | 6 ++-- substrate/frame/broker/src/types.rs | 13 +++++--- substrate/frame/broker/src/weights.rs | 16 +++++----- 9 files changed, 66 insertions(+), 59 deletions(-) diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs index 89b1c4c86632..5c9175a18d98 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/weights/pallet_broker.rs @@ -154,8 +154,8 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:1 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:2) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:0 w:1) @@ -337,8 +337,8 @@ impl pallet_broker::WeightInfo for WeightInfo { } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:1) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:1) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: // Measured: `957` diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs index 13d5fcf3898b..7e1c832a9092 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/weights/pallet_broker.rs @@ -152,8 +152,8 @@ impl pallet_broker::WeightInfo for WeightInfo { /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:1 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:2) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:0) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:0 w:1) @@ -335,8 +335,8 @@ impl pallet_broker::WeightInfo for WeightInfo { } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:1) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:1) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: // Measured: `556` diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 7533e3dc68c4..ca3c9ba7be98 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -288,8 +288,8 @@ mod benches { #[extrinsic_call] _(RawOrigin::Signed(caller), region.core); - let id = AllowedRenewalId { core: region.core, when: region.begin + region_len * 2 }; - assert!(AllowedRenewals::::get(id).is_some()); + let id = PotentialRenewalId { core: region.core, when: region.begin + region_len * 2 }; + assert!(PotentialRenewals::::get(id).is_some()); Ok(()) } @@ -670,20 +670,20 @@ mod benches { (T::TimeslicePeriod::get() * (region_len * 3).into()).try_into().ok().unwrap(), ); - let id = AllowedRenewalId { core, when }; - let record = AllowedRenewalRecord { + let id = PotentialRenewalId { core, when }; + let record = PotentialRenewalRecord { price: 1u32.into(), completion: CompletionStatus::Complete(new_schedule()), }; - AllowedRenewals::::insert(id, record); + PotentialRenewals::::insert(id, record); let caller: T::AccountId = whitelisted_caller(); #[extrinsic_call] _(RawOrigin::Signed(caller), core, when); - assert!(AllowedRenewals::::get(id).is_none()); - assert_last_event::(Event::AllowedRenewalDropped { core, when }.into()); + assert!(PotentialRenewals::::get(id).is_none()); + assert_last_event::(Event::PotentialRenewalDropped { core, when }.into()); Ok(()) } diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 45a0a514c307..2b0aa5d1bc23 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -135,7 +135,7 @@ impl Pallet { Ok(id) } - /// Must be called on a core in `AllowedRenewals` whose value is a timeslice equal to the + /// Must be called on a core in `PotentialRenewals` whose value is a timeslice equal to the /// current sale status's `region_end`. pub(crate) fn do_renew(who: T::AccountId, core: CoreIndex) -> Result { let config = Configuration::::get().ok_or(Error::::Uninitialized)?; @@ -143,8 +143,8 @@ impl Pallet { let mut sale = SaleInfo::::get().ok_or(Error::::NoSales)?; Self::ensure_cores_for_sale(&status, &sale)?; - let renewal_id = AllowedRenewalId { core, when: sale.region_begin }; - let record = AllowedRenewals::::get(renewal_id).ok_or(Error::::NotAllowed)?; + let renewal_id = PotentialRenewalId { core, when: sale.region_begin }; + let record = PotentialRenewals::::get(renewal_id).ok_or(Error::::NotAllowed)?; let workload = record.completion.drain_complete().ok_or(Error::::IncompleteAssignment)?; @@ -169,9 +169,9 @@ impl Pallet { let price_cap = record.price + config.renewal_bump * record.price; let now = frame_system::Pallet::::block_number(); let price = Self::sale_price(&sale, now).min(price_cap); - let new_record = AllowedRenewalRecord { price, completion: Complete(workload) }; - AllowedRenewals::::remove(renewal_id); - AllowedRenewals::::insert(AllowedRenewalId { core, when: begin }, &new_record); + let new_record = PotentialRenewalRecord { price, completion: Complete(workload) }; + PotentialRenewals::::remove(renewal_id); + PotentialRenewals::::insert(PotentialRenewalId { core, when: begin }, &new_record); SaleInfo::::put(&sale); if let Some(workload) = new_record.completion.drain_complete() { Self::deposit_event(Event::Renewable { core, price, begin, workload }); @@ -281,17 +281,19 @@ impl Pallet { let duration = region.end.saturating_sub(region_id.begin); if duration == config.region_length && finality == Finality::Final { if let Some(price) = region.paid { - let renewal_id = AllowedRenewalId { core: region_id.core, when: region.end }; - let assigned = match AllowedRenewals::::get(renewal_id) { - Some(AllowedRenewalRecord { completion: Partial(w), price: p }) + let renewal_id = PotentialRenewalId { core: region_id.core, when: region.end }; + let assigned = match PotentialRenewals::::get(renewal_id) { + Some(PotentialRenewalRecord { completion: Partial(w), price: p }) if price == p => w, _ => CoreMask::void(), } | region_id.mask; let workload = if assigned.is_complete() { Complete(workplan) } else { Partial(assigned) }; - let record = AllowedRenewalRecord { price, completion: workload }; - AllowedRenewals::::insert(&renewal_id, &record); + let record = PotentialRenewalRecord { price, completion: workload }; + // Note: This entry alone does not yet actually allow renewals (the completion + // status has to be complete for `do_renew` to accept it). + PotentialRenewals::::insert(&renewal_id, &record); if let Some(workload) = record.completion.drain_complete() { Self::deposit_event(Event::Renewable { core: region_id.core, @@ -444,10 +446,10 @@ impl Pallet { pub(crate) fn do_drop_renewal(core: CoreIndex, when: Timeslice) -> DispatchResult { let status = Status::::get().ok_or(Error::::Uninitialized)?; ensure!(status.last_committed_timeslice >= when, Error::::StillValid); - let id = AllowedRenewalId { core, when }; - ensure!(AllowedRenewals::::contains_key(id), Error::::UnknownRenewal); - AllowedRenewals::::remove(id); - Self::deposit_event(Event::AllowedRenewalDropped { core, when }); + let id = PotentialRenewalId { core, when }; + ensure!(PotentialRenewals::::contains_key(id), Error::::UnknownRenewal); + PotentialRenewals::::remove(id); + Self::deposit_event(Event::PotentialRenewalDropped { core, when }); Ok(()) } diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index d59c4c9c6b24..c194635d0092 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -136,10 +136,12 @@ pub mod pallet { #[pallet::storage] pub type SaleInfo = StorageValue<_, SaleInfoRecordOf, OptionQuery>; - /// Records of allowed renewals. + /// Records of potential renewals. + /// + /// Renewals will only actually be allowed if `CompletionStatus` is actually `Complete`. #[pallet::storage] - pub type AllowedRenewals = - StorageMap<_, Twox64Concat, AllowedRenewalId, AllowedRenewalRecordOf, OptionQuery>; + pub type PotentialRenewals = + StorageMap<_, Twox64Concat, PotentialRenewalId, PotentialRenewalRecordOf, OptionQuery>; /// The current (unassigned or provisionally assigend) Regions. #[pallet::storage] @@ -413,7 +415,7 @@ pub mod pallet { assignment: Vec<(CoreAssignment, PartsOf57600)>, }, /// Some historical Instantaneous Core Pool payment record has been dropped. - AllowedRenewalDropped { + PotentialRenewalDropped { /// The timeslice whose renewal is no longer available. when: Timeslice, /// The core whose workload is no longer available to be renewed for `when`. diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index f929f0d50dcf..809a5ea69214 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -78,9 +78,9 @@ fn drop_renewal_works() { let e = Error::::StillValid; assert_noop!(Broker::do_drop_renewal(region.core, region.begin + 3), e); advance_to(12); - assert_eq!(AllowedRenewals::::iter().count(), 1); + assert_eq!(PotentialRenewals::::iter().count(), 1); assert_ok!(Broker::do_drop_renewal(region.core, region.begin + 3)); - assert_eq!(AllowedRenewals::::iter().count(), 0); + assert_eq!(PotentialRenewals::::iter().count(), 0); let e = Error::::UnknownRenewal; assert_noop!(Broker::do_drop_renewal(region.core, region.begin + 3), e); }); @@ -927,12 +927,12 @@ fn leases_can_be_renewed() { // Start the sales with only one core for this lease. assert_ok!(Broker::do_start_sales(100, 0)); - // Advance to sale period 1, we should get an AllowedRenewal for task 2001 for the next + // Advance to sale period 1, we should get an PotentialRenewal for task 2001 for the next // sale. advance_sale_period(); assert_eq!( - AllowedRenewals::::get(AllowedRenewalId { core: 0, when: 10 }), - Some(AllowedRenewalRecord { + PotentialRenewals::::get(PotentialRenewalId { core: 0, when: 10 }), + Some(PotentialRenewalRecord { price: 100, completion: CompletionStatus::Complete( vec![ScheduleItem { mask: CoreMask::complete(), assignment: Task(2001) }] @@ -1023,14 +1023,14 @@ fn short_leases_cannot_be_renewed() { // The lease is removed. assert_eq!(Leases::::get().len(), 0); - // We should have got an entry in AllowedRenewals, but we don't because rotate_sale + // We should have got an entry in PotentialRenewals, but we don't because rotate_sale // schedules leases a period in advance. This renewal should be in the period after next // because while bootstrapping our way into the sale periods, we give everything a lease for // period 1, so they can renew for period 2. So we have a core until the end of period 1, // but we are not marked as able to renew because we expired before sale period 1 starts. // // This should be fixed. - assert_eq!(AllowedRenewals::::get(AllowedRenewalId { core: 0, when: 10 }), None); + assert_eq!(PotentialRenewals::::get(PotentialRenewalId { core: 0, when: 10 }), None); // And the lease has been removed from storage. assert_eq!(Leases::::get().len(), 0); @@ -1163,11 +1163,11 @@ fn renewal_requires_valid_status_and_sale_info() { assert_ok!(Broker::do_start_sales(200, 1)); assert_noop!(Broker::do_renew(1, 1), Error::::NotAllowed); - let record = AllowedRenewalRecord { + let record = PotentialRenewalRecord { price: 100, completion: CompletionStatus::Partial(CoreMask::from_chunk(0, 20)), }; - AllowedRenewals::::insert(AllowedRenewalId { core: 1, when: 4 }, &record); + PotentialRenewals::::insert(PotentialRenewalId { core: 1, when: 4 }, &record); assert_noop!(Broker::do_renew(1, 1), Error::::IncompleteAssignment); }); } diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 04e9a65bf8f6..e05fb695cda9 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -220,9 +220,9 @@ impl Pallet { let expire = until < region_end; if expire { // last time for this one - make it renewable in the next sale. - let renewal_id = AllowedRenewalId { core: first_core, when: region_end }; - let record = AllowedRenewalRecord { price, completion: Complete(schedule) }; - AllowedRenewals::::insert(renewal_id, &record); + let renewal_id = PotentialRenewalId { core: first_core, when: region_end }; + let record = PotentialRenewalRecord { price, completion: Complete(schedule) }; + PotentialRenewals::::insert(renewal_id, &record); Self::deposit_event(Event::Renewable { core: first_core, price, diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index f2cae9a41ad4..3c2e12d196d6 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -152,25 +152,28 @@ impl CompletionStatus { } } -/// The identity of a possible Core workload renewal. +/// The identity of a possibly Core workload renewal. #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct AllowedRenewalId { +pub struct PotentialRenewalId { /// The core whose workload at the sale ending with `when` may be renewed to begin at `when`. pub core: CoreIndex, /// The point in time that the renewable workload on `core` ends and a fresh renewal may begin. pub when: Timeslice, } -/// A record of an allowed renewal. +/// A record of a potential renewal. +/// +/// The renewal will only actually be allowed if `CompletionStatus` is `Complete` at the time of +/// renewal. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct AllowedRenewalRecord { +pub struct PotentialRenewalRecord { /// The price for which the next renewal can be made. pub price: Balance, /// The workload which will be scheduled on the Core in the case a renewal is made, or if /// incomplete, then the parts of the core which have been scheduled. pub completion: CompletionStatus, } -pub type AllowedRenewalRecordOf = AllowedRenewalRecord>; +pub type PotentialRenewalRecordOf = PotentialRenewalRecord>; /// General status of the system. #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index 2aa1c282a41d..234a64fc3521 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -178,8 +178,8 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:1 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:2) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) /// Storage: `Authorship::Author` (r:1 w:0) /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `System::Digest` (r:1 w:0) @@ -342,8 +342,8 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:1) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:1) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: // Measured: `661` @@ -585,8 +585,8 @@ impl WeightInfo for () { /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:1 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:2) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:2) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) /// Storage: `Authorship::Author` (r:1 w:0) /// Proof: `Authorship::Author` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `System::Digest` (r:1 w:0) @@ -749,8 +749,8 @@ impl WeightInfo for () { } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) - /// Storage: `Broker::AllowedRenewals` (r:1 w:1) - /// Proof: `Broker::AllowedRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) + /// Storage: `Broker::PotentialRenewals` (r:1 w:1) + /// Proof: `Broker::PotentialRenewals` (`max_values`: None, `max_size`: Some(1233), added: 3708, mode: `MaxEncodedLen`) fn drop_renewal() -> Weight { // Proof Size summary in bytes: // Measured: `661` From 2855832d9fc3a00b6ae3c9e449aa08ffbda38956 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 20 May 2024 14:14:05 +0200 Subject: [PATCH 03/33] WIP: New price based `AdaptPrice` trait. --- substrate/frame/broker/src/adapt_price.rs | 37 ++++++++++++++------ substrate/frame/broker/src/lib.rs | 3 +- substrate/frame/broker/src/tick_impls.rs | 42 +++++++---------------- substrate/frame/broker/src/types.rs | 10 +++--- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 79f6f77bd5b1..ce2002aa55f5 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -17,20 +17,33 @@ #![deny(missing_docs)] -use crate::CoreIndex; +use crate::{CoreIndex, SaleInfoRecord}; use sp_arithmetic::{traits::One, FixedU64}; use sp_runtime::Saturating; -struct SalePerformance { +pub struct SalePerformance { /// The price at which the last core was sold. /// /// Will be `None` if no cores have been offered. - purchase_price: Option, + sellout_price: Option, /// The base price (lowest possible price) that was used in this sale. price: Balance, } +/// Result of `AdaptPrice::adapt_price`. +pub struct AdaptedPrices { + /// New base price to use. + price: Balance, + /// Price to use for renewals of leases. + renewal_price: Balance, +} + +impl From> for SalePerformance { + fn from(record: SaleInfoRecord) -> Self { + Self { sellout_price: record.sellout_price, price: record.price } + } +} /// Type for determining how to set price. pub trait AdaptPrice { @@ -39,28 +52,30 @@ pub trait AdaptPrice { /// - `when`: The amount through the leadin period; between zero and one. fn leadin_factor_at(when: FixedU64) -> FixedU64; - /// Return the base price that should be used in the next sale, based on this sale's - /// performance. - fn adapt_price(SalePerformance) -> Balance; + /// Return adapted prices for next sale. + /// + /// Based on this sale's performance. + fn adapt_price(performance: SalePerformance) -> AdaptedPrices; } -impl AdaptPrice for () { +impl AdaptPrice for () { fn leadin_factor_at(_: FixedU64) -> FixedU64 { FixedU64::one() } - fn adapt_price(_: CoreIndex, _: CoreIndex, _: CoreIndex) -> FixedU64 { - FixedU64::one() + fn adapt_price(performance: SalePerformance) -> AdaptedPrices { + let price = performance.sellout_price.unwrap_or(performance.price); + AdaptedPrices { price, renewal_price: price } } } /// Simple implementation of `AdaptPrice` giving a monotonic leadin and a linear price change based /// on cores sold. pub struct Linear; -impl AdaptPrice for Linear { +impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { FixedU64::from(2).saturating_sub(when) } - fn adapt_price(sold: CoreIndex, target: CoreIndex, limit: CoreIndex) -> FixedU64 { + fn adapt_price(performance: SalePerformance) -> AdaptedPrices { if sold <= target { // Range of [0.5, 1.0]. FixedU64::from_rational(1, 2).saturating_add(FixedU64::from_rational( diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index c194635d0092..3819a3347121 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -298,8 +298,7 @@ pub mod pallet { /// The timeslice on which the Regions which are being sold in the sale terminate. /// (i.e. One after the last timeslice which the Regions control.) region_end: Timeslice, - /// The number of cores we want to sell, ideally. Selling this amount would result in - /// no change to the price for the next sale. + /// The number of cores we want to sell, ideally. ideal_cores_sold: CoreIndex, /// Number of cores which are/have been offered for sale. cores_offered: CoreIndex, diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index e05fb695cda9..b22f11b33b06 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -163,31 +163,7 @@ impl Pallet { InstaPoolIo::::mutate(old_sale.region_end, |r| r.system.saturating_reduce(old_pooled)); // Calculate the start price for the upcoming sale. - let price = { - let offered = old_sale.cores_offered; - let ideal = old_sale.ideal_cores_sold; - let sold = old_sale.cores_sold; - - let maybe_purchase_price = if offered == 0 { - // No cores offered for sale - no purchase price. - None - } else if sold >= ideal { - // Sold more than the ideal amount. We should look for the last purchase price - // before the sell-out. If there was no purchase at all, then we avoid having a - // price here so that we make no alterations to it (since otherwise we would - // increase it). - old_sale.sellout_price - } else { - // Sold less than the ideal - we fall back to the regular price. - Some(old_sale.price) - }; - if let Some(purchase_price) = maybe_purchase_price { - T::PriceAdapter::adapt_price(sold.min(offered), ideal, offered) - .saturating_mul_int(purchase_price) - } else { - old_sale.price - } - }; + let new_prices = T::PriceAdapter::adapt_price(old_sale.into()); // Set workload for the reserved (system, probably) workloads. let region_begin = old_sale.region_end; @@ -221,11 +197,14 @@ impl Pallet { if expire { // last time for this one - make it renewable in the next sale. let renewal_id = PotentialRenewalId { core: first_core, when: region_end }; - let record = PotentialRenewalRecord { price, completion: Complete(schedule) }; + let record = PotentialRenewalRecord { + price: new_prices.renewal_price, + completion: Complete(schedule), + }; PotentialRenewals::::insert(renewal_id, &record); Self::deposit_event(Event::Renewable { core: first_core, - price, + price: new_prices.renewal_price, begin: region_end, workload: record.completion.drain_complete().unwrap_or_default(), }); @@ -245,10 +224,11 @@ impl Pallet { let leadin_length = config.leadin_length; let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; // Update SaleInfo - let new_sale = SaleInfoRecord { + let mut new_sale = SaleInfoRecord { sale_start, leadin_length, - price, + price: new_prices.price, + // None for now, we adjust below: sellout_price: None, region_begin, region_end, @@ -257,6 +237,10 @@ impl Pallet { cores_offered, cores_sold: 0, }; + + if cores_offered > 0 { + new_sale.sellout_price = Some(Self::sale_price(&new_sale, new_sale.sale_start)); + } SaleInfo::::put(&new_sale); Self::deposit_event(Event::SaleInitialized { sale_start, diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index 3c2e12d196d6..7b6b6cc0ca4b 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -228,8 +228,9 @@ pub struct SaleInfoRecord { /// The index of the first core which is for sale. Core of Regions which are sold have /// incrementing indices from this. pub first_core: CoreIndex, - /// The latest price at which Bulk Coretime was purchased until surpassing the ideal number of - /// cores were sold. + /// The price at which cores have been sold out. + /// + /// Will only be `None` if no core was offered for sale. pub sellout_price: Option, /// Number of cores which have been sold; never more than cores_offered. pub cores_sold: CoreIndex, @@ -266,8 +267,9 @@ pub struct ConfigRecord { pub leadin_length: BlockNumber, /// The length in timeslices of Regions which are up for sale in forthcoming sales. pub region_length: Timeslice, - /// The proportion of cores available for sale which should be sold in order for the price - /// to remain the same in the next sale. + /// The proportion of cores available for sale which should be sold. + /// + /// If more cores are sold than this, then the price will not be adjusted downwards further. pub ideal_bulk_proportion: Perbill, /// An artificial limit to the number of cores which are allowed to be sold. If `Some` then /// no more cores will be sold than this. From 627acf79fe440b4aed4177f241e78adf1222ee53 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 08:52:35 +0200 Subject: [PATCH 04/33] Pub --- substrate/frame/broker/src/adapt_price.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index ce2002aa55f5..5a32a4148fad 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -25,18 +25,18 @@ pub struct SalePerformance { /// The price at which the last core was sold. /// /// Will be `None` if no cores have been offered. - sellout_price: Option, + pub sellout_price: Option, /// The base price (lowest possible price) that was used in this sale. - price: Balance, + pub price: Balance, } /// Result of `AdaptPrice::adapt_price`. pub struct AdaptedPrices { /// New base price to use. - price: Balance, + pub price: Balance, /// Price to use for renewals of leases. - renewal_price: Balance, + pub renewal_price: Balance, } impl From> for SalePerformance { From 31ea3a9e7be69c958c7d224968def9114f9f6f22 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 11:01:12 +0200 Subject: [PATCH 05/33] Also adjust sellout_price on renew. --- substrate/frame/broker/src/dispatchable_impls.rs | 15 +++++---------- substrate/frame/broker/src/utility_impls.rs | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 2b0aa5d1bc23..7a88f74fa983 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -121,12 +121,8 @@ impl Pallet { let price = Self::sale_price(&sale, now); ensure!(price_limit >= price, Error::::Overpriced); - Self::charge(&who, price)?; - let core = sale.first_core.saturating_add(sale.cores_sold); - sale.cores_sold.saturating_inc(); - if sale.cores_sold <= sale.ideal_cores_sold || sale.sellout_price.is_none() { - sale.sellout_price = Some(price); - } + let core = Self::purchase_core(&who, price, &mut sale)?; + SaleInfo::::put(&sale); let id = Self::issue(core, sale.region_begin, sale.region_end, Some(who.clone()), Some(price)); @@ -149,8 +145,9 @@ impl Pallet { record.completion.drain_complete().ok_or(Error::::IncompleteAssignment)?; let old_core = core; - let core = sale.first_core.saturating_add(sale.cores_sold); - Self::charge(&who, record.price)?; + + let core = Self::purchase_core(&who, record.price, &mut sale)?; + Self::deposit_event(Event::Renewed { who, old_core, @@ -161,8 +158,6 @@ impl Pallet { workload: workload.clone(), }); - sale.cores_sold.saturating_inc(); - Workplan::::insert((sale.region_begin, core), &workload); let begin = sale.region_end; diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 4163817a8b58..2e15ed0d8d2e 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -72,6 +72,21 @@ impl Pallet { Ok(()) } + /// Buy a core at the specified price (price is to be determined by the caller). + pub(crate) fn purchase_core( + who: &T::AccountId, + price: BalanceOf, + sale: &mut SaleInfoRecordOf, + ) -> Result { + Self::charge(who, price)?; + let core = sale.first_core.saturating_add(sale.cores_sold); + sale.cores_sold.saturating_inc(); + if sale.cores_sold <= sale.ideal_cores_sold || sale.sellout_price.is_none() { + sale.sellout_price = Some(price); + } + Ok(core) + } + pub fn issue( core: CoreIndex, begin: Timeslice, From 63f58569b3950721180a527d39d80fc9c5eee13b Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 11:01:44 +0200 Subject: [PATCH 06/33] Adapt price implementation. --- substrate/frame/broker/src/adapt_price.rs | 54 +++++++++++++---------- substrate/frame/broker/src/lib.rs | 2 +- substrate/frame/broker/src/tick_impls.rs | 24 +++++----- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 5a32a4148fad..3a5f66087c0d 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -17,10 +17,11 @@ #![deny(missing_docs)] -use crate::{CoreIndex, SaleInfoRecord}; +use crate::SaleInfoRecord; use sp_arithmetic::{traits::One, FixedU64}; -use sp_runtime::Saturating; +use sp_runtime::{FixedPointNumber, FixedPointOperand, Saturating}; +/// Performance of a past sale. pub struct SalePerformance { /// The price at which the last core was sold. /// @@ -39,8 +40,9 @@ pub struct AdaptedPrices { pub renewal_price: Balance, } -impl From> for SalePerformance { - fn from(record: SaleInfoRecord) -> Self { +impl SalePerformance { + /// Construct performance via data from a `SaleInfoRecord`. + pub fn from_sale(record: &SaleInfoRecord) -> Self { Self { sellout_price: record.sellout_price, price: record.price } } } @@ -58,7 +60,7 @@ pub trait AdaptPrice { fn adapt_price(performance: SalePerformance) -> AdaptedPrices; } -impl AdaptPrice for () { +impl AdaptPrice for () { fn leadin_factor_at(_: FixedU64) -> FixedU64 { FixedU64::one() } @@ -70,28 +72,32 @@ impl AdaptPrice for () { /// Simple implementation of `AdaptPrice` giving a monotonic leadin and a linear price change based /// on cores sold. -pub struct Linear; -impl AdaptPrice for Linear { +pub struct Linear(std::marker::PhantomData); +impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { - FixedU64::from(2).saturating_sub(when) + FixedU64::from(101).saturating_sub(when.saturating_mul(100.into())) } + fn adapt_price(performance: SalePerformance) -> AdaptedPrices { - if sold <= target { - // Range of [0.5, 1.0]. - FixedU64::from_rational(1, 2).saturating_add(FixedU64::from_rational( - sold.into(), - target.saturating_mul(2).into(), - )) - } else { - // Range of (1.0, 2]. - - // Unchecked math: In this branch we know that sold > target. The limit must be >= sold - // by construction, and thus target must be < limit. - FixedU64::one().saturating_add(FixedU64::from_rational( - (sold - target).into(), - (limit - target).into(), - )) - } + let leadin_max = Self::leadin_factor_at(0.into()); + let leadin_min = Self::leadin_factor_at(1.into()); + let spread = leadin_max.saturating_sub(leadin_min); + + let Some(sellout_price) = performance.sellout_price else { + return AdaptedPrices { + price: performance.price, + renewal_price: spread + .saturating_add(2.into()) + .div(2.into()) + .saturating_mul_int(performance.price), + } + }; + + let price = FixedU64::from(2) + .div(spread.saturating_add(2.into())) + .saturating_mul_int(sellout_price); + + AdaptedPrices { price, renewal_price: sellout_price } } } diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index 3819a3347121..405d9811a438 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -92,7 +92,7 @@ pub mod pallet { type Coretime: CoretimeInterface; /// The algorithm to determine the next price on the basis of market performance. - type PriceAdapter: AdaptPrice; + type PriceAdapter: AdaptPrice>; /// Reversible conversion from local balance to Relay-chain balance. This will typically be /// the `Identity`, but provided just in case the chains use different representations. diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index b22f11b33b06..6be988fc8eed 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -17,10 +17,7 @@ use super::*; use frame_support::{pallet_prelude::*, weights::WeightMeter}; -use sp_arithmetic::{ - traits::{One, SaturatedConversion, Saturating, Zero}, - FixedPointNumber, -}; +use sp_arithmetic::traits::{One, SaturatedConversion, Saturating, Zero}; use sp_runtime::traits::ConvertBack; use sp_std::{vec, vec::Vec}; use CompletionStatus::Complete; @@ -163,7 +160,7 @@ impl Pallet { InstaPoolIo::::mutate(old_sale.region_end, |r| r.system.saturating_reduce(old_pooled)); // Calculate the start price for the upcoming sale. - let new_prices = T::PriceAdapter::adapt_price(old_sale.into()); + let new_prices = T::PriceAdapter::adapt_price(SalePerformance::from_sale(&old_sale)); // Set workload for the reserved (system, probably) workloads. let region_begin = old_sale.region_end; @@ -223,13 +220,19 @@ impl Pallet { let sale_start = now.saturating_add(config.interlude_length); let leadin_length = config.leadin_length; let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; + let sellout_price = if cores_offered > 0 { + // No core sold -> price was too high. + Some(new_prices.price) + } else { + None + }; + // Update SaleInfo - let mut new_sale = SaleInfoRecord { + let new_sale = SaleInfoRecord { sale_start, leadin_length, price: new_prices.price, - // None for now, we adjust below: - sellout_price: None, + sellout_price, region_begin, region_end, first_core, @@ -238,15 +241,12 @@ impl Pallet { cores_sold: 0, }; - if cores_offered > 0 { - new_sale.sellout_price = Some(Self::sale_price(&new_sale, new_sale.sale_start)); - } SaleInfo::::put(&new_sale); Self::deposit_event(Event::SaleInitialized { sale_start, leadin_length, start_price: Self::sale_price(&new_sale, now), - regular_price: price, + regular_price: new_prices.price, region_begin, region_end, ideal_cores_sold, From 3630cccc8fbc8f555581b882de3de811d35f9b40 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 13:16:38 +0200 Subject: [PATCH 07/33] Some tests + handle going down to zero gracefully. --- substrate/frame/broker/src/adapt_price.rs | 92 ++++++++++++++++------- substrate/frame/broker/src/mock.rs | 2 +- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 3a5f66087c0d..7fc0b419dff1 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -22,6 +22,7 @@ use sp_arithmetic::{traits::One, FixedU64}; use sp_runtime::{FixedPointNumber, FixedPointOperand, Saturating}; /// Performance of a past sale. +#[derive(Copy, Clone)] pub struct SalePerformance { /// The price at which the last core was sold. /// @@ -33,6 +34,7 @@ pub struct SalePerformance { } /// Result of `AdaptPrice::adapt_price`. +#[derive(Copy, Clone)] pub struct AdaptedPrices { /// New base price to use. pub price: Balance, @@ -73,6 +75,7 @@ impl AdaptPrice for () { /// Simple implementation of `AdaptPrice` giving a monotonic leadin and a linear price change based /// on cores sold. pub struct Linear(std::marker::PhantomData); + impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { FixedU64::from(101).saturating_sub(when.saturating_mul(100.into())) @@ -97,6 +100,13 @@ impl AdaptPrice for Linear { .div(spread.saturating_add(2.into())) .saturating_mul_int(sellout_price); + let price = if price == Balance::zero() { + // We could not recover from a price equal 0 ever. + sellout_price + } else { + price + }; + AdaptedPrices { price, renewal_price: sellout_price } } } @@ -107,37 +117,65 @@ mod tests { #[test] fn linear_no_panic() { - for limit in 0..10 { - for target in 1..10 { - for sold in 0..=limit { - let price = Linear::adapt_price(sold, target, limit); - - if sold > target { - assert!(price > FixedU64::one()); - } else { - assert!(price <= FixedU64::one()); - } - } + for sellout in 0..11 { + for price in 0..10 { + let sellout_price = if sellout == 11 { None } else { Some(sellout) }; + Linear::adapt_price(SalePerformance { sellout_price, price }); } } } #[test] - fn linear_bound_check() { - // Using constraints from pallet implementation i.e. `limit >= sold`. - // Check extremes - let limit = 10; - let target = 5; - - // Maximally sold: `sold == limit` - assert_eq!(Linear::adapt_price(limit, target, limit), FixedU64::from_float(2.0)); - // Ideally sold: `sold == target` - assert_eq!(Linear::adapt_price(target, target, limit), FixedU64::one()); - // Minimally sold: `sold == 0` - assert_eq!(Linear::adapt_price(0, target, limit), FixedU64::from_float(0.5)); - // Optimistic target: `target == limit` - assert_eq!(Linear::adapt_price(limit, limit, limit), FixedU64::one()); - // Pessimistic target: `target == 0` - assert_eq!(Linear::adapt_price(limit, 0, limit), FixedU64::from_float(2.0)); + fn no_op_sale_is_good() { + let prices = Linear::adapt_price(SalePerformance { sellout_price: None, price: 1 }); + assert_eq!(prices.renewal_price, 51); + assert_eq!(prices.price, 1); + } + + #[test] + fn price_stays_stable_on_optimal_sale() { + // Check price stays stable if sold at the optimal price: + let mut performance = SalePerformance { sellout_price: Some(5100), price: 100 }; + for _ in 0..10 { + let prices = Linear::adapt_price(performance); + performance.sellout_price = Some(5100); + performance.price = prices.price; + + assert!(prices.price <= 101); + assert!(prices.price >= 99); + assert!(prices.renewal_price <= 5101); + assert!(prices.renewal_price >= 5099); + } + } + + #[test] + fn price_never_goes_to_zero_and_recovers() { + // Check price stays stable if sold at the optimal price: + let sellout_price = 51; + let mut performance = SalePerformance { sellout_price: Some(sellout_price), price: 1 }; + for _ in 0..11 { + let prices = Linear::adapt_price(performance); + performance.sellout_price = Some(sellout_price); + performance.price = prices.price; + + assert!(prices.price <= sellout_price); + assert!(prices.price > 0); + } } + // Using constraints from pallet implementation i.e. `limit >= sold`. + // Check extremes + // let limit = 10; + // let target = 5; + + // Maximally sold: `sold == limit` + // assert_eq!(Linear::adapt_price(limit, target, limit), FixedU64::from_float(2.0)); + // Ideally sold: `sold == target` + // assert_eq!(Linear::adapt_price(target, target, limit), FixedU64::one()); + // Minimally sold: `sold == 0` + // assert_eq!(Linear::adapt_price(0, target, limit), FixedU64::from_float(0.5)); + // Optimistic target: `target == limit` + // assert_eq!(Linear::adapt_price(limit, limit, limit), FixedU64::one()); + // Pessimistic target: `target == 0` + // assert_eq!(Linear::adapt_price(limit, 0, limit), FixedU64::from_float(2.0)); + // } } diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index 6219b4eff1b4..6681fca0daba 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -199,7 +199,7 @@ impl crate::Config for Test { type WeightInfo = (); type PalletId = TestBrokerId; type AdminOrigin = EnsureOneOrRoot; - type PriceAdapter = Linear; + type PriceAdapter = Linear>; } pub fn advance_to(b: u64) { From b615fbe0f9b4aa5c2eafcc3512f96054795b5356 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 18:06:08 +0200 Subject: [PATCH 08/33] Fix tests. --- substrate/frame/broker/src/tests.rs | 30 ++++++++++++++---------- substrate/frame/broker/src/tick_impls.rs | 2 +- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 809a5ea69214..b54ab25d3071 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -361,22 +361,27 @@ fn migration_works() { #[test] fn renewal_works() { - TestExt::new().endow(1, 1000).execute_with(|| { + let b = 100_000; + TestExt::new().endow(1, b).execute_with(move || { assert_ok!(Broker::do_start_sales(100, 1)); advance_to(2); let region = Broker::do_purchase(1, u64::max_value()).unwrap(); - assert_eq!(balance(1), 900); + // Price is lower, because already two blocks in: + let b = b - 100; + assert_eq!(balance(1), b); assert_ok!(Broker::do_assign(region, None, 1001, Final)); // Should now be renewable. advance_to(6); assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::TooEarly); let core = Broker::do_renew(1, region.core).unwrap(); - assert_eq!(balance(1), 800); + let b = b - 100; + assert_eq!(balance(1), b); advance_to(8); assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::SoldOut); advance_to(12); assert_ok!(Broker::do_renew(1, core)); - assert_eq!(balance(1), 690); + let b = b - 101; + assert_eq!(balance(1), b); }); } @@ -916,7 +921,8 @@ fn short_leases_are_cleaned() { #[test] fn leases_can_be_renewed() { - TestExt::new().endow(1, 1000).execute_with(|| { + let initial_balance = 100_000; + TestExt::new().endow(1, initial_balance).execute_with(|| { // Timeslice period is 2. // // Sale 1 starts at block 7, Sale 2 starts at 13. @@ -933,7 +939,7 @@ fn leases_can_be_renewed() { assert_eq!( PotentialRenewals::::get(PotentialRenewalId { core: 0, when: 10 }), Some(PotentialRenewalRecord { - price: 100, + price: 5100, completion: CompletionStatus::Complete( vec![ScheduleItem { mask: CoreMask::complete(), assignment: Task(2001) }] .try_into() @@ -947,8 +953,8 @@ fn leases_can_be_renewed() { // Advance to sale period 2, where we can renew. advance_sale_period(); assert_ok!(Broker::do_renew(1, 0)); - // We renew for the base price of the previous sale period. - assert_eq!(balance(1), 900); + // We renew for the price of the previous sale period. + assert_eq!(balance(1), initial_balance - 5100); // We just renewed for this period. advance_sale_period(); @@ -1274,7 +1280,7 @@ fn config_works() { /// Ensure that a lease that ended before `start_sales` was called can be renewed. #[test] fn renewal_works_leases_ended_before_start_sales() { - TestExt::new().endow(1, 1000).execute_with(|| { + TestExt::new().endow(1, 100_000).execute_with(|| { let config = Configuration::::get().unwrap(); // This lease is ended before `start_stales` was called. @@ -1304,7 +1310,7 @@ fn renewal_works_leases_ended_before_start_sales() { let new_core = Broker::do_renew(1, 0).unwrap(); // Renewing the active lease doesn't work. assert_noop!(Broker::do_renew(1, 1), Error::::SoldOut); - assert_eq!(balance(1), 900); + assert_eq!(balance(1), 94900); // This intializes the third sale and the period 2. advance_sale_period(); @@ -1312,7 +1318,7 @@ fn renewal_works_leases_ended_before_start_sales() { // Renewing the active lease doesn't work. assert_noop!(Broker::do_renew(1, 0), Error::::SoldOut); - assert_eq!(balance(1), 800); + assert_eq!(balance(1), 94800); // All leases should have ended assert!(Leases::::get().is_empty()); @@ -1324,7 +1330,7 @@ fn renewal_works_leases_ended_before_start_sales() { assert_eq!(0, Broker::do_renew(1, new_core).unwrap()); // Renew the task 2. assert_eq!(1, Broker::do_renew(1, 0).unwrap()); - assert_eq!(balance(1), 600); + assert_eq!(balance(1), 94699); // This intializes the fifth sale and the period 4. advance_sale_period(); diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 6be988fc8eed..a5450da02591 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -221,7 +221,7 @@ impl Pallet { let leadin_length = config.leadin_length; let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; let sellout_price = if cores_offered > 0 { - // No core sold -> price was too high. + // No core sold -> price was too high -> we have to adjust downwards. Some(new_prices.price) } else { None From fd03dc177c7e26399ddcfeaa51d1fc6f90ed2018 Mon Sep 17 00:00:00 2001 From: eskimor Date: Tue, 21 May 2024 18:14:13 +0200 Subject: [PATCH 09/33] More linear tests. --- substrate/frame/broker/src/adapt_price.rs | 47 +++++++++++++++-------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 7fc0b419dff1..7ca7dce332d9 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -148,6 +148,22 @@ mod tests { } } + #[test] + fn price_adjusts_correctly_upwards() { + let performance = SalePerformance { sellout_price: Some(10_100), price: 100 }; + let prices = Linear::adapt_price(performance); + assert_eq!(prices.renewal_price, 10_100); + assert_eq!(prices.price, 2 * 10_100 / 102); + } + + #[test] + fn price_adjusts_correctly_downwards() { + let performance = SalePerformance { sellout_price: Some(100), price: 100 }; + let prices = Linear::adapt_price(performance); + assert_eq!(prices.renewal_price, 100); + assert_eq!(prices.price, 2 * 100 / 102); + } + #[test] fn price_never_goes_to_zero_and_recovers() { // Check price stays stable if sold at the optimal price: @@ -162,20 +178,19 @@ mod tests { assert!(prices.price > 0); } } - // Using constraints from pallet implementation i.e. `limit >= sold`. - // Check extremes - // let limit = 10; - // let target = 5; - - // Maximally sold: `sold == limit` - // assert_eq!(Linear::adapt_price(limit, target, limit), FixedU64::from_float(2.0)); - // Ideally sold: `sold == target` - // assert_eq!(Linear::adapt_price(target, target, limit), FixedU64::one()); - // Minimally sold: `sold == 0` - // assert_eq!(Linear::adapt_price(0, target, limit), FixedU64::from_float(0.5)); - // Optimistic target: `target == limit` - // assert_eq!(Linear::adapt_price(limit, limit, limit), FixedU64::one()); - // Pessimistic target: `target == 0` - // assert_eq!(Linear::adapt_price(limit, 0, limit), FixedU64::from_float(2.0)); - // } + + #[test] + fn renewal_price_is_correct_on_no_sale() { + let performance = SalePerformance { sellout_price: None, price: 100 }; + let prices = Linear::adapt_price(performance); + assert_eq!(prices.renewal_price, 5100); + assert_eq!(prices.price, 100); + } + + #[test] + fn renewal_price_is_sell_out() { + let performance = SalePerformance { sellout_price: Some(1000), price: 100 }; + let prices = Linear::adapt_price(performance); + assert_eq!(prices.renewal_price, 1000); + } } From 6fdc11bb4542c617ba11f08c85273fe98799e48b Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 22 May 2024 08:07:31 +0200 Subject: [PATCH 10/33] Add prdoc. --- prdoc/pr_4521.prdoc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 prdoc/pr_4521.prdoc diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc new file mode 100644 index 000000000000..6417c52b2417 --- /dev/null +++ b/prdoc/pr_4521.prdoc @@ -0,0 +1,23 @@ +title: Broker pallet: AdaptPrice price controller is now price controlled. + +doc: + - audience: Runtime Dev + description: | + We discovered a flaw of the current price controller interface in the + broker pallet. This is fixed by changing the interface to no longer + operate on the number of cores sold, but rather on the price that was + achieved during the sale. More information here: + + https://github.com/paritytech/polkadot-sdk/issues/4360 + + - audience: Runtime User + description: | + The price controller of the Coretime chain will be adjusted with this + release. This will very likely be used in the fellowship production + runtime to have a much larger leadin. This fixes a price manipulation + issue we discovered with the Kusama launch. + +crates: + - name: pallet-broker + bump: major + From cb5485d18b05e0d35a5098d5276dd2de8f49766f Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 23 May 2024 10:34:18 +0200 Subject: [PATCH 11/33] Put base price in the middle of the range. h = 10 * o o = 10 * b --- substrate/frame/broker/src/adapt_price.rs | 38 +++++++++-------------- substrate/frame/broker/src/tests.rs | 20 +++++------- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 7ca7dce332d9..4ec5aa44aac2 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -78,28 +78,18 @@ pub struct Linear(std::marker::PhantomData); impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { - FixedU64::from(101).saturating_sub(when.saturating_mul(100.into())) + FixedU64::from(100).saturating_sub(when.saturating_mul(99.into())) } fn adapt_price(performance: SalePerformance) -> AdaptedPrices { - let leadin_max = Self::leadin_factor_at(0.into()); - let leadin_min = Self::leadin_factor_at(1.into()); - let spread = leadin_max.saturating_sub(leadin_min); - let Some(sellout_price) = performance.sellout_price else { return AdaptedPrices { price: performance.price, - renewal_price: spread - .saturating_add(2.into()) - .div(2.into()) - .saturating_mul_int(performance.price), + renewal_price: FixedU64::from(10).saturating_mul_int(performance.price), } }; - let price = FixedU64::from(2) - .div(spread.saturating_add(2.into())) - .saturating_mul_int(sellout_price); - + let price = FixedU64::from_rational(1, 10).saturating_mul_int(sellout_price); let price = if price == Balance::zero() { // We could not recover from a price equal 0 ever. sellout_price @@ -128,32 +118,32 @@ mod tests { #[test] fn no_op_sale_is_good() { let prices = Linear::adapt_price(SalePerformance { sellout_price: None, price: 1 }); - assert_eq!(prices.renewal_price, 51); + assert_eq!(prices.renewal_price, 10); assert_eq!(prices.price, 1); } #[test] fn price_stays_stable_on_optimal_sale() { // Check price stays stable if sold at the optimal price: - let mut performance = SalePerformance { sellout_price: Some(5100), price: 100 }; + let mut performance = SalePerformance { sellout_price: Some(1000), price: 100 }; for _ in 0..10 { let prices = Linear::adapt_price(performance); - performance.sellout_price = Some(5100); + performance.sellout_price = Some(1000); performance.price = prices.price; assert!(prices.price <= 101); assert!(prices.price >= 99); - assert!(prices.renewal_price <= 5101); - assert!(prices.renewal_price >= 5099); + assert!(prices.renewal_price <= 1001); + assert!(prices.renewal_price >= 999); } } #[test] fn price_adjusts_correctly_upwards() { - let performance = SalePerformance { sellout_price: Some(10_100), price: 100 }; + let performance = SalePerformance { sellout_price: Some(10_000), price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 10_100); - assert_eq!(prices.price, 2 * 10_100 / 102); + assert_eq!(prices.renewal_price, 10_000); + assert_eq!(prices.price, 1000); } #[test] @@ -161,13 +151,13 @@ mod tests { let performance = SalePerformance { sellout_price: Some(100), price: 100 }; let prices = Linear::adapt_price(performance); assert_eq!(prices.renewal_price, 100); - assert_eq!(prices.price, 2 * 100 / 102); + assert_eq!(prices.price, 10); } #[test] fn price_never_goes_to_zero_and_recovers() { // Check price stays stable if sold at the optimal price: - let sellout_price = 51; + let sellout_price = 1; let mut performance = SalePerformance { sellout_price: Some(sellout_price), price: 1 }; for _ in 0..11 { let prices = Linear::adapt_price(performance); @@ -183,7 +173,7 @@ mod tests { fn renewal_price_is_correct_on_no_sale() { let performance = SalePerformance { sellout_price: None, price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 5100); + assert_eq!(prices.renewal_price, 1000); assert_eq!(prices.price, 100); } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index b54ab25d3071..7d033bd71f80 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -366,22 +366,18 @@ fn renewal_works() { assert_ok!(Broker::do_start_sales(100, 1)); advance_to(2); let region = Broker::do_purchase(1, u64::max_value()).unwrap(); - // Price is lower, because already two blocks in: - let b = b - 100; - assert_eq!(balance(1), b); + assert_eq!(balance(1), 99_900); assert_ok!(Broker::do_assign(region, None, 1001, Final)); // Should now be renewable. advance_to(6); assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::TooEarly); let core = Broker::do_renew(1, region.core).unwrap(); - let b = b - 100; - assert_eq!(balance(1), b); + assert_eq!(balance(1), 99_800); advance_to(8); assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::SoldOut); advance_to(12); assert_ok!(Broker::do_renew(1, core)); - let b = b - 101; - assert_eq!(balance(1), b); + assert_eq!(balance(1), 99_690); }); } @@ -939,7 +935,7 @@ fn leases_can_be_renewed() { assert_eq!( PotentialRenewals::::get(PotentialRenewalId { core: 0, when: 10 }), Some(PotentialRenewalRecord { - price: 5100, + price: 1000, completion: CompletionStatus::Complete( vec![ScheduleItem { mask: CoreMask::complete(), assignment: Task(2001) }] .try_into() @@ -954,7 +950,7 @@ fn leases_can_be_renewed() { advance_sale_period(); assert_ok!(Broker::do_renew(1, 0)); // We renew for the price of the previous sale period. - assert_eq!(balance(1), initial_balance - 5100); + assert_eq!(balance(1), initial_balance - 1000); // We just renewed for this period. advance_sale_period(); @@ -1310,7 +1306,7 @@ fn renewal_works_leases_ended_before_start_sales() { let new_core = Broker::do_renew(1, 0).unwrap(); // Renewing the active lease doesn't work. assert_noop!(Broker::do_renew(1, 1), Error::::SoldOut); - assert_eq!(balance(1), 94900); + assert_eq!(balance(1), 99000); // This intializes the third sale and the period 2. advance_sale_period(); @@ -1318,7 +1314,7 @@ fn renewal_works_leases_ended_before_start_sales() { // Renewing the active lease doesn't work. assert_noop!(Broker::do_renew(1, 0), Error::::SoldOut); - assert_eq!(balance(1), 94800); + assert_eq!(balance(1), 98900); // All leases should have ended assert!(Leases::::get().is_empty()); @@ -1330,7 +1326,7 @@ fn renewal_works_leases_ended_before_start_sales() { assert_eq!(0, Broker::do_renew(1, new_core).unwrap()); // Renew the task 2. assert_eq!(1, Broker::do_renew(1, 0).unwrap()); - assert_eq!(balance(1), 94699); + assert_eq!(balance(1), 98790); // This intializes the fifth sale and the period 4. advance_sale_period(); From 9299010f959a1615e804b527ac1191a13c23d943 Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 23 May 2024 12:56:07 +0200 Subject: [PATCH 12/33] new leadin curve. --- substrate/frame/broker/src/adapt_price.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 4ec5aa44aac2..a5394d9edb45 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -38,7 +38,7 @@ pub struct SalePerformance { pub struct AdaptedPrices { /// New base price to use. pub price: Balance, - /// Price to use for renewals of leases. + /// Price we optimize for. pub renewal_price: Balance, } @@ -78,7 +78,11 @@ pub struct Linear(std::marker::PhantomData); impl AdaptPrice for Linear { fn leadin_factor_at(when: FixedU64) -> FixedU64 { - FixedU64::from(100).saturating_sub(when.saturating_mul(99.into())) + if when <= FixedU64::from_rational(1, 2) { + FixedU64::from(100).saturating_sub(when.saturating_mul(180.into())) + } else { + FixedU64::from(19).saturating_sub(when.saturating_mul(18.into())) + } } fn adapt_price(performance: SalePerformance) -> AdaptedPrices { From 28f13f463cc2e1fb4953221c6dd178b82daed074 Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 23 May 2024 16:04:17 +0200 Subject: [PATCH 13/33] price -> base_price --- substrate/frame/broker/src/adapt_price.rs | 68 +++++++++---------- .../frame/broker/src/dispatchable_impls.rs | 6 +- substrate/frame/broker/src/lib.rs | 6 +- substrate/frame/broker/src/tests.rs | 4 +- substrate/frame/broker/src/tick_impls.rs | 10 +-- substrate/frame/broker/src/types.rs | 2 +- substrate/frame/broker/src/utility_impls.rs | 2 +- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index a5394d9edb45..0f049548ec56 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -30,22 +30,22 @@ pub struct SalePerformance { pub sellout_price: Option, /// The base price (lowest possible price) that was used in this sale. - pub price: Balance, + pub base_price: Balance, } /// Result of `AdaptPrice::adapt_price`. #[derive(Copy, Clone)] pub struct AdaptedPrices { /// New base price to use. - pub price: Balance, + pub base_price: Balance, /// Price we optimize for. - pub renewal_price: Balance, + pub target_price: Balance, } impl SalePerformance { /// Construct performance via data from a `SaleInfoRecord`. pub fn from_sale(record: &SaleInfoRecord) -> Self { - Self { sellout_price: record.sellout_price, price: record.price } + Self { sellout_price: record.sellout_price, base_price: record.base_price } } } @@ -67,8 +67,8 @@ impl AdaptPrice for () { FixedU64::one() } fn adapt_price(performance: SalePerformance) -> AdaptedPrices { - let price = performance.sellout_price.unwrap_or(performance.price); - AdaptedPrices { price, renewal_price: price } + let price = performance.sellout_price.unwrap_or(performance.base_price); + AdaptedPrices { base_price: price, target_price: price } } } @@ -88,8 +88,8 @@ impl AdaptPrice for Linear { fn adapt_price(performance: SalePerformance) -> AdaptedPrices { let Some(sellout_price) = performance.sellout_price else { return AdaptedPrices { - price: performance.price, - renewal_price: FixedU64::from(10).saturating_mul_int(performance.price), + base_price: performance.base_price, + target_price: FixedU64::from(10).saturating_mul_int(performance.base_price), } }; @@ -101,7 +101,7 @@ impl AdaptPrice for Linear { price }; - AdaptedPrices { price, renewal_price: sellout_price } + AdaptedPrices { base_price: price, target_price: sellout_price } } } @@ -114,77 +114,77 @@ mod tests { for sellout in 0..11 { for price in 0..10 { let sellout_price = if sellout == 11 { None } else { Some(sellout) }; - Linear::adapt_price(SalePerformance { sellout_price, price }); + Linear::adapt_price(SalePerformance { sellout_price, base_price: price }); } } } #[test] fn no_op_sale_is_good() { - let prices = Linear::adapt_price(SalePerformance { sellout_price: None, price: 1 }); - assert_eq!(prices.renewal_price, 10); - assert_eq!(prices.price, 1); + let prices = Linear::adapt_price(SalePerformance { sellout_price: None, base_price: 1 }); + assert_eq!(prices.target_price, 10); + assert_eq!(prices.base_price, 1); } #[test] fn price_stays_stable_on_optimal_sale() { // Check price stays stable if sold at the optimal price: - let mut performance = SalePerformance { sellout_price: Some(1000), price: 100 }; + let mut performance = SalePerformance { sellout_price: Some(1000), base_price: 100 }; for _ in 0..10 { let prices = Linear::adapt_price(performance); performance.sellout_price = Some(1000); - performance.price = prices.price; + performance.base_price = prices.base_price; - assert!(prices.price <= 101); - assert!(prices.price >= 99); - assert!(prices.renewal_price <= 1001); - assert!(prices.renewal_price >= 999); + assert!(prices.base_price <= 101); + assert!(prices.base_price >= 99); + assert!(prices.target_price <= 1001); + assert!(prices.target_price >= 999); } } #[test] fn price_adjusts_correctly_upwards() { - let performance = SalePerformance { sellout_price: Some(10_000), price: 100 }; + let performance = SalePerformance { sellout_price: Some(10_000), base_price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 10_000); - assert_eq!(prices.price, 1000); + assert_eq!(prices.target_price, 10_000); + assert_eq!(prices.base_price, 1000); } #[test] fn price_adjusts_correctly_downwards() { - let performance = SalePerformance { sellout_price: Some(100), price: 100 }; + let performance = SalePerformance { sellout_price: Some(100), base_price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 100); - assert_eq!(prices.price, 10); + assert_eq!(prices.target_price, 100); + assert_eq!(prices.base_price, 10); } #[test] fn price_never_goes_to_zero_and_recovers() { // Check price stays stable if sold at the optimal price: let sellout_price = 1; - let mut performance = SalePerformance { sellout_price: Some(sellout_price), price: 1 }; + let mut performance = SalePerformance { sellout_price: Some(sellout_price), base_price: 1 }; for _ in 0..11 { let prices = Linear::adapt_price(performance); performance.sellout_price = Some(sellout_price); - performance.price = prices.price; + performance.base_price = prices.base_price; - assert!(prices.price <= sellout_price); - assert!(prices.price > 0); + assert!(prices.base_price <= sellout_price); + assert!(prices.base_price > 0); } } #[test] fn renewal_price_is_correct_on_no_sale() { - let performance = SalePerformance { sellout_price: None, price: 100 }; + let performance = SalePerformance { sellout_price: None, base_price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 1000); - assert_eq!(prices.price, 100); + assert_eq!(prices.target_price, 1000); + assert_eq!(prices.base_price, 100); } #[test] fn renewal_price_is_sell_out() { - let performance = SalePerformance { sellout_price: Some(1000), price: 100 }; + let performance = SalePerformance { sellout_price: Some(1000), base_price: 100 }; let prices = Linear::adapt_price(performance); - assert_eq!(prices.renewal_price, 1000); + assert_eq!(prices.target_price, 1000); } } diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 7a88f74fa983..d742675f8d67 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -70,7 +70,7 @@ impl Pallet { Ok(()) } - pub(crate) fn do_start_sales(price: BalanceOf, extra_cores: CoreIndex) -> DispatchResult { + pub(crate) fn do_start_sales(base_price: BalanceOf, extra_cores: CoreIndex) -> DispatchResult { let config = Configuration::::get().ok_or(Error::::Uninitialized)?; // Determine the core count @@ -93,7 +93,7 @@ impl Pallet { let old_sale = SaleInfoRecord { sale_start: now, leadin_length: Zero::zero(), - price, + base_price, sellout_price: None, region_begin: commit_timeslice, region_end: commit_timeslice.saturating_add(config.region_length), @@ -102,7 +102,7 @@ impl Pallet { cores_offered: 0, cores_sold: 0, }; - Self::deposit_event(Event::::SalesStarted { price, core_count }); + Self::deposit_event(Event::::SalesStarted { price: base_price, core_count }); Self::rotate_sale(old_sale, &config, &status); Status::::put(&status); Ok(()) diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index 405d9811a438..e399cc0f06bf 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -559,7 +559,7 @@ pub mod pallet { /// Begin the Bulk Coretime sales rotation. /// /// - `origin`: Must be Root or pass `AdminOrigin`. - /// - `initial_price`: The price of Bulk Coretime in the first sale. + /// - `base_price`: The price after the leadin period of Bulk Coretime in the first sale. /// - `extra_cores`: Number of extra cores that should be requested on top of the cores /// required for `Reservations` and `Leases`. /// @@ -571,11 +571,11 @@ pub mod pallet { ))] pub fn start_sales( origin: OriginFor, - initial_price: BalanceOf, + base_price: BalanceOf, extra_cores: CoreIndex, ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin_or_root(origin)?; - Self::do_start_sales(initial_price, extra_cores)?; + Self::do_start_sales(base_price, extra_cores)?; Ok(Pays::No.into()) } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 7d033bd71f80..d62f1cac12aa 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -1104,7 +1104,7 @@ fn purchase_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - price: 200, + base_price: 200, sellout_price: None, region_begin: 0, region_end: 3, @@ -1146,7 +1146,7 @@ fn renewal_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - price: 200, + base_price: 200, sellout_price: None, region_begin: 0, region_end: 3, diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index a5450da02591..8d304f7bcf57 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -195,13 +195,13 @@ impl Pallet { // last time for this one - make it renewable in the next sale. let renewal_id = PotentialRenewalId { core: first_core, when: region_end }; let record = PotentialRenewalRecord { - price: new_prices.renewal_price, + price: new_prices.target_price, completion: Complete(schedule), }; PotentialRenewals::::insert(renewal_id, &record); Self::deposit_event(Event::Renewable { core: first_core, - price: new_prices.renewal_price, + price: new_prices.target_price, begin: region_end, workload: record.completion.drain_complete().unwrap_or_default(), }); @@ -222,7 +222,7 @@ impl Pallet { let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; let sellout_price = if cores_offered > 0 { // No core sold -> price was too high -> we have to adjust downwards. - Some(new_prices.price) + Some(new_prices.base_price) } else { None }; @@ -231,7 +231,7 @@ impl Pallet { let new_sale = SaleInfoRecord { sale_start, leadin_length, - price: new_prices.price, + base_price: new_prices.base_price, sellout_price, region_begin, region_end, @@ -246,7 +246,7 @@ impl Pallet { sale_start, leadin_length, start_price: Self::sale_price(&new_sale, now), - regular_price: new_prices.price, + regular_price: new_prices.base_price, region_begin, region_end, ideal_cores_sold, diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index 7b6b6cc0ca4b..08f4df93111c 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -214,7 +214,7 @@ pub struct SaleInfoRecord { /// The length in blocks of the Leadin Period (where the price is decreasing). pub leadin_length: BlockNumber, /// The price of Bulk Coretime after the Leadin Period. - pub price: Balance, + pub base_price: Balance, /// The first timeslice of the Regions which are being sold in this sale. pub region_begin: Timeslice, /// The timeslice on which the Regions which are being sold in the sale terminate. (i.e. One diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 2e15ed0d8d2e..5e32d65de94d 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -63,7 +63,7 @@ impl Pallet { pub fn sale_price(sale: &SaleInfoRecordOf, now: BlockNumberFor) -> BalanceOf { let num = now.saturating_sub(sale.sale_start).min(sale.leadin_length).saturated_into(); let through = FixedU64::from_rational(num, sale.leadin_length.saturated_into()); - T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.price) + T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.base_price) } pub(crate) fn charge(who: &T::AccountId, amount: BalanceOf) -> DispatchResult { From 4b5a66dd3ac3a06b3939cbf320020e3dabb13c24 Mon Sep 17 00:00:00 2001 From: eskimor Date: Thu, 23 May 2024 16:04:46 +0200 Subject: [PATCH 14/33] Fmt --- substrate/frame/broker/src/dispatchable_impls.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index d742675f8d67..efd84738accc 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -70,7 +70,10 @@ impl Pallet { Ok(()) } - pub(crate) fn do_start_sales(base_price: BalanceOf, extra_cores: CoreIndex) -> DispatchResult { + pub(crate) fn do_start_sales( + base_price: BalanceOf, + extra_cores: CoreIndex, + ) -> DispatchResult { let config = Configuration::::get().ok_or(Error::::Uninitialized)?; // Determine the core count From fad4aab6b476a2c381ede0dfadd9688a28f5cb7a Mon Sep 17 00:00:00 2001 From: eskimor Date: Fri, 24 May 2024 11:26:59 +0200 Subject: [PATCH 15/33] base_price -> min_price --- substrate/frame/broker/src/adapt_price.rs | 56 +++++++++---------- .../frame/broker/src/dispatchable_impls.rs | 6 +- substrate/frame/broker/src/lib.rs | 6 +- substrate/frame/broker/src/tests.rs | 4 +- substrate/frame/broker/src/tick_impls.rs | 6 +- substrate/frame/broker/src/types.rs | 8 ++- substrate/frame/broker/src/utility_impls.rs | 5 +- 7 files changed, 48 insertions(+), 43 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 0f049548ec56..b48a7a52ebc6 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -29,15 +29,15 @@ pub struct SalePerformance { /// Will be `None` if no cores have been offered. pub sellout_price: Option, - /// The base price (lowest possible price) that was used in this sale. - pub base_price: Balance, + /// The minimum price that was achieved in this sale. + pub min_price: Balance, } /// Result of `AdaptPrice::adapt_price`. #[derive(Copy, Clone)] pub struct AdaptedPrices { - /// New base price to use. - pub base_price: Balance, + /// New minimum price to use. + pub min_price: Balance, /// Price we optimize for. pub target_price: Balance, } @@ -45,7 +45,7 @@ pub struct AdaptedPrices { impl SalePerformance { /// Construct performance via data from a `SaleInfoRecord`. pub fn from_sale(record: &SaleInfoRecord) -> Self { - Self { sellout_price: record.sellout_price, base_price: record.base_price } + Self { sellout_price: record.sellout_price, min_price: record.min_price } } } @@ -67,8 +67,8 @@ impl AdaptPrice for () { FixedU64::one() } fn adapt_price(performance: SalePerformance) -> AdaptedPrices { - let price = performance.sellout_price.unwrap_or(performance.base_price); - AdaptedPrices { base_price: price, target_price: price } + let price = performance.sellout_price.unwrap_or(performance.min_price); + AdaptedPrices { min_price: price, target_price: price } } } @@ -88,8 +88,8 @@ impl AdaptPrice for Linear { fn adapt_price(performance: SalePerformance) -> AdaptedPrices { let Some(sellout_price) = performance.sellout_price else { return AdaptedPrices { - base_price: performance.base_price, - target_price: FixedU64::from(10).saturating_mul_int(performance.base_price), + min_price: performance.min_price, + target_price: FixedU64::from(10).saturating_mul_int(performance.min_price), } }; @@ -101,7 +101,7 @@ impl AdaptPrice for Linear { price }; - AdaptedPrices { base_price: price, target_price: sellout_price } + AdaptedPrices { min_price: price, target_price: sellout_price } } } @@ -114,29 +114,29 @@ mod tests { for sellout in 0..11 { for price in 0..10 { let sellout_price = if sellout == 11 { None } else { Some(sellout) }; - Linear::adapt_price(SalePerformance { sellout_price, base_price: price }); + Linear::adapt_price(SalePerformance { sellout_price, min_price: price }); } } } #[test] fn no_op_sale_is_good() { - let prices = Linear::adapt_price(SalePerformance { sellout_price: None, base_price: 1 }); + let prices = Linear::adapt_price(SalePerformance { sellout_price: None, min_price: 1 }); assert_eq!(prices.target_price, 10); - assert_eq!(prices.base_price, 1); + assert_eq!(prices.min_price, 1); } #[test] fn price_stays_stable_on_optimal_sale() { // Check price stays stable if sold at the optimal price: - let mut performance = SalePerformance { sellout_price: Some(1000), base_price: 100 }; + let mut performance = SalePerformance { sellout_price: Some(1000), min_price: 100 }; for _ in 0..10 { let prices = Linear::adapt_price(performance); performance.sellout_price = Some(1000); - performance.base_price = prices.base_price; + performance.min_price = prices.min_price; - assert!(prices.base_price <= 101); - assert!(prices.base_price >= 99); + assert!(prices.min_price <= 101); + assert!(prices.min_price >= 99); assert!(prices.target_price <= 1001); assert!(prices.target_price >= 999); } @@ -144,46 +144,46 @@ mod tests { #[test] fn price_adjusts_correctly_upwards() { - let performance = SalePerformance { sellout_price: Some(10_000), base_price: 100 }; + let performance = SalePerformance { sellout_price: Some(10_000), min_price: 100 }; let prices = Linear::adapt_price(performance); assert_eq!(prices.target_price, 10_000); - assert_eq!(prices.base_price, 1000); + assert_eq!(prices.min_price, 1000); } #[test] fn price_adjusts_correctly_downwards() { - let performance = SalePerformance { sellout_price: Some(100), base_price: 100 }; + let performance = SalePerformance { sellout_price: Some(100), min_price: 100 }; let prices = Linear::adapt_price(performance); assert_eq!(prices.target_price, 100); - assert_eq!(prices.base_price, 10); + assert_eq!(prices.min_price, 10); } #[test] fn price_never_goes_to_zero_and_recovers() { // Check price stays stable if sold at the optimal price: let sellout_price = 1; - let mut performance = SalePerformance { sellout_price: Some(sellout_price), base_price: 1 }; + let mut performance = SalePerformance { sellout_price: Some(sellout_price), min_price: 1 }; for _ in 0..11 { let prices = Linear::adapt_price(performance); performance.sellout_price = Some(sellout_price); - performance.base_price = prices.base_price; + performance.min_price = prices.min_price; - assert!(prices.base_price <= sellout_price); - assert!(prices.base_price > 0); + assert!(prices.min_price <= sellout_price); + assert!(prices.min_price > 0); } } #[test] fn renewal_price_is_correct_on_no_sale() { - let performance = SalePerformance { sellout_price: None, base_price: 100 }; + let performance = SalePerformance { sellout_price: None, min_price: 100 }; let prices = Linear::adapt_price(performance); assert_eq!(prices.target_price, 1000); - assert_eq!(prices.base_price, 100); + assert_eq!(prices.min_price, 100); } #[test] fn renewal_price_is_sell_out() { - let performance = SalePerformance { sellout_price: Some(1000), base_price: 100 }; + let performance = SalePerformance { sellout_price: Some(1000), min_price: 100 }; let prices = Linear::adapt_price(performance); assert_eq!(prices.target_price, 1000); } diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index efd84738accc..83c91270bdba 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -71,7 +71,7 @@ impl Pallet { } pub(crate) fn do_start_sales( - base_price: BalanceOf, + min_price: BalanceOf, extra_cores: CoreIndex, ) -> DispatchResult { let config = Configuration::::get().ok_or(Error::::Uninitialized)?; @@ -96,7 +96,7 @@ impl Pallet { let old_sale = SaleInfoRecord { sale_start: now, leadin_length: Zero::zero(), - base_price, + min_price, sellout_price: None, region_begin: commit_timeslice, region_end: commit_timeslice.saturating_add(config.region_length), @@ -105,7 +105,7 @@ impl Pallet { cores_offered: 0, cores_sold: 0, }; - Self::deposit_event(Event::::SalesStarted { price: base_price, core_count }); + Self::deposit_event(Event::::SalesStarted { price: min_price, core_count }); Self::rotate_sale(old_sale, &config, &status); Status::::put(&status); Ok(()) diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index e399cc0f06bf..f313544db56e 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -559,7 +559,7 @@ pub mod pallet { /// Begin the Bulk Coretime sales rotation. /// /// - `origin`: Must be Root or pass `AdminOrigin`. - /// - `base_price`: The price after the leadin period of Bulk Coretime in the first sale. + /// - `min_price`: The price after the leadin period of Bulk Coretime in the first sale. /// - `extra_cores`: Number of extra cores that should be requested on top of the cores /// required for `Reservations` and `Leases`. /// @@ -571,11 +571,11 @@ pub mod pallet { ))] pub fn start_sales( origin: OriginFor, - base_price: BalanceOf, + min_price: BalanceOf, extra_cores: CoreIndex, ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin_or_root(origin)?; - Self::do_start_sales(base_price, extra_cores)?; + Self::do_start_sales(min_price, extra_cores)?; Ok(Pays::No.into()) } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index d62f1cac12aa..18c6543d900b 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -1104,7 +1104,7 @@ fn purchase_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - base_price: 200, + min_price: 200, sellout_price: None, region_begin: 0, region_end: 3, @@ -1146,7 +1146,7 @@ fn renewal_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - base_price: 200, + min_price: 200, sellout_price: None, region_begin: 0, region_end: 3, diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 8d304f7bcf57..6743468a5b82 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -222,7 +222,7 @@ impl Pallet { let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; let sellout_price = if cores_offered > 0 { // No core sold -> price was too high -> we have to adjust downwards. - Some(new_prices.base_price) + Some(new_prices.min_price) } else { None }; @@ -231,7 +231,7 @@ impl Pallet { let new_sale = SaleInfoRecord { sale_start, leadin_length, - base_price: new_prices.base_price, + min_price: new_prices.min_price, sellout_price, region_begin, region_end, @@ -246,7 +246,7 @@ impl Pallet { sale_start, leadin_length, start_price: Self::sale_price(&new_sale, now), - regular_price: new_prices.base_price, + regular_price: new_prices.min_price, region_begin, region_end, ideal_cores_sold, diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index 08f4df93111c..7c9b5b85055f 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -152,7 +152,7 @@ impl CompletionStatus { } } -/// The identity of a possibly Core workload renewal. +/// The identity of a possibly renewable Core workload. #[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct PotentialRenewalId { /// The core whose workload at the sale ending with `when` may be renewed to begin at `when`. @@ -214,7 +214,7 @@ pub struct SaleInfoRecord { /// The length in blocks of the Leadin Period (where the price is decreasing). pub leadin_length: BlockNumber, /// The price of Bulk Coretime after the Leadin Period. - pub base_price: Balance, + pub min_price: Balance, /// The first timeslice of the Regions which are being sold in this sale. pub region_begin: Timeslice, /// The timeslice on which the Regions which are being sold in the sale terminate. (i.e. One @@ -269,7 +269,9 @@ pub struct ConfigRecord { pub region_length: Timeslice, /// The proportion of cores available for sale which should be sold. /// - /// If more cores are sold than this, then the price will not be adjusted downwards further. + /// If more cores are sold than this, then further sales will no longer be considered in + /// determining the sellout price. In other words the sellout price will be the last price + /// paid, without going over this limit. pub ideal_bulk_proportion: Perbill, /// An artificial limit to the number of cores which are allowed to be sold. If `Some` then /// no more cores will be sold than this. diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 5e32d65de94d..68bd74387234 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -63,7 +63,7 @@ impl Pallet { pub fn sale_price(sale: &SaleInfoRecordOf, now: BlockNumberFor) -> BalanceOf { let num = now.saturating_sub(sale.sale_start).min(sale.leadin_length).saturated_into(); let through = FixedU64::from_rational(num, sale.leadin_length.saturated_into()); - T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.base_price) + T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.min_price) } pub(crate) fn charge(who: &T::AccountId, amount: BalanceOf) -> DispatchResult { @@ -73,6 +73,9 @@ impl Pallet { } /// Buy a core at the specified price (price is to be determined by the caller). + /// + /// Note: It is the responsibility of the caller to write back the changed `SaleInfoRecordOf` to + /// storage. pub(crate) fn purchase_core( who: &T::AccountId, price: BalanceOf, From 5869882120b33f9398553bc81af5b9564dc6b809 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 13:04:15 +0200 Subject: [PATCH 16/33] New example adapter name + more parameters (cores) --- .../coretime/coretime-rococo/src/coretime.rs | 2 +- .../coretime/coretime-westend/src/coretime.rs | 2 +- substrate/bin/node/runtime/src/lib.rs | 2 +- substrate/frame/broker/src/adapt_price.rs | 64 +++++++++++++------ substrate/frame/broker/src/mock.rs | 2 +- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index 742dd50f6fa1..ff78e49e3959 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -232,5 +232,5 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::Linear; + type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs index 41cbc62fa211..97bee8afb88f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -245,5 +245,5 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::Linear; + type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 7d9128bb940a..b02e50fb5bae 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2145,7 +2145,7 @@ impl pallet_broker::Config for Runtime { type WeightInfo = (); type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::Linear; + type PriceAdapter = pallet_broker::CenterTargetPrice; } parameter_types! { diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index b48a7a52ebc6..774318d1c579 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -17,7 +17,7 @@ #![deny(missing_docs)] -use crate::SaleInfoRecord; +use crate::{CoreIndex, SaleInfoRecord}; use sp_arithmetic::{traits::One, FixedU64}; use sp_runtime::{FixedPointNumber, FixedPointOperand, Saturating}; @@ -31,6 +31,15 @@ pub struct SalePerformance { /// The minimum price that was achieved in this sale. pub min_price: Balance, + + /// The number of cores we want to sell, ideally. + pub ideal_cores_sold: CoreIndex, + + /// Number of cores which are/have been offered for sale. + pub cores_offered: CoreIndex, + + /// Number of cores which have been sold; never more than cores_offered. + pub cores_sold: CoreIndex, } /// Result of `AdaptPrice::adapt_price`. @@ -45,7 +54,18 @@ pub struct AdaptedPrices { impl SalePerformance { /// Construct performance via data from a `SaleInfoRecord`. pub fn from_sale(record: &SaleInfoRecord) -> Self { - Self { sellout_price: record.sellout_price, min_price: record.min_price } + Self { + sellout_price: record.sellout_price, + min_price: record.min_price, + ideal_cores_sold: record.ideal_cores_sold, + cores_offered: record.cores_offered, + cores_sold: record.cores_sold, + } + } + + #[cfg(test)] + fn new(sellout_price: Option, min_price: Balance) -> Self { + Self { sellout_price, min_price, ideal_cores_sold: 0, cores_offered: 0, cores_sold: 0 } } } @@ -58,7 +78,7 @@ pub trait AdaptPrice { /// Return adapted prices for next sale. /// - /// Based on this sale's performance. + /// Based on the previous sale's performance. fn adapt_price(performance: SalePerformance) -> AdaptedPrices; } @@ -72,11 +92,13 @@ impl AdaptPrice for () { } } -/// Simple implementation of `AdaptPrice` giving a monotonic leadin and a linear price change based -/// on cores sold. -pub struct Linear(std::marker::PhantomData); +/// Simple implementation of `AdaptPrice` with two linear phases. +/// +/// One steep one downwards to the target price, which is 1/10 of the maximum price and a more flat +/// one down to the minimum price, which is 1/100 of the maximum price. +pub struct CenterTargetPrice(core::marker::PhantomData); -impl AdaptPrice for Linear { +impl AdaptPrice for CenterTargetPrice { fn leadin_factor_at(when: FixedU64) -> FixedU64 { if when <= FixedU64::from_rational(1, 2) { FixedU64::from(100).saturating_sub(when.saturating_mul(180.into())) @@ -114,14 +136,14 @@ mod tests { for sellout in 0..11 { for price in 0..10 { let sellout_price = if sellout == 11 { None } else { Some(sellout) }; - Linear::adapt_price(SalePerformance { sellout_price, min_price: price }); + CenterTargetPrice::adapt_price(SalePerformance::new(sellout_price, price)); } } } #[test] fn no_op_sale_is_good() { - let prices = Linear::adapt_price(SalePerformance { sellout_price: None, min_price: 1 }); + let prices = CenterTargetPrice::adapt_price(SalePerformance::new(None, 1)); assert_eq!(prices.target_price, 10); assert_eq!(prices.min_price, 1); } @@ -129,9 +151,9 @@ mod tests { #[test] fn price_stays_stable_on_optimal_sale() { // Check price stays stable if sold at the optimal price: - let mut performance = SalePerformance { sellout_price: Some(1000), min_price: 100 }; + let mut performance = SalePerformance::new(Some(1000), 100); for _ in 0..10 { - let prices = Linear::adapt_price(performance); + let prices = CenterTargetPrice::adapt_price(performance); performance.sellout_price = Some(1000); performance.min_price = prices.min_price; @@ -144,16 +166,16 @@ mod tests { #[test] fn price_adjusts_correctly_upwards() { - let performance = SalePerformance { sellout_price: Some(10_000), min_price: 100 }; - let prices = Linear::adapt_price(performance); + let performance = SalePerformance::new(Some(10_000), 100); + let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 10_000); assert_eq!(prices.min_price, 1000); } #[test] fn price_adjusts_correctly_downwards() { - let performance = SalePerformance { sellout_price: Some(100), min_price: 100 }; - let prices = Linear::adapt_price(performance); + let performance = SalePerformance::new(Some(100), 100); + let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 100); assert_eq!(prices.min_price, 10); } @@ -162,9 +184,9 @@ mod tests { fn price_never_goes_to_zero_and_recovers() { // Check price stays stable if sold at the optimal price: let sellout_price = 1; - let mut performance = SalePerformance { sellout_price: Some(sellout_price), min_price: 1 }; + let mut performance = SalePerformance::new(Some(sellout_price), 1); for _ in 0..11 { - let prices = Linear::adapt_price(performance); + let prices = CenterTargetPrice::adapt_price(performance); performance.sellout_price = Some(sellout_price); performance.min_price = prices.min_price; @@ -175,16 +197,16 @@ mod tests { #[test] fn renewal_price_is_correct_on_no_sale() { - let performance = SalePerformance { sellout_price: None, min_price: 100 }; - let prices = Linear::adapt_price(performance); + let performance = SalePerformance::new(None, 100); + let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 1000); assert_eq!(prices.min_price, 100); } #[test] fn renewal_price_is_sell_out() { - let performance = SalePerformance { sellout_price: Some(1000), min_price: 100 }; - let prices = Linear::adapt_price(performance); + let performance = SalePerformance::new(Some(1000), 100); + let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 1000); } } diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index 6681fca0daba..da8943497ebc 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -199,7 +199,7 @@ impl crate::Config for Test { type WeightInfo = (); type PalletId = TestBrokerId; type AdminOrigin = EnsureOneOrRoot; - type PriceAdapter = Linear>; + type PriceAdapter = CenterTargetPrice>; } pub fn advance_to(b: u64) { From baeab0ef61e71d2d2f5b5ab3effc9b23ece383f5 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 15:45:28 +0200 Subject: [PATCH 17/33] Fix kitchensink --- substrate/bin/node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index b02e50fb5bae..801abc28d3dd 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -2145,7 +2145,7 @@ impl pallet_broker::Config for Runtime { type WeightInfo = (); type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::CenterTargetPrice; + type PriceAdapter = pallet_broker::CenterTargetPrice; } parameter_types! { From aa4defc2c28ffaabbc8edf9ae8107146aa260c08 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 15:48:32 +0200 Subject: [PATCH 18/33] Some logging + test. --- Cargo.lock | 1 + substrate/frame/broker/Cargo.toml | 1 + .../frame/broker/src/dispatchable_impls.rs | 7 ++ substrate/frame/broker/src/mock.rs | 4 ++ substrate/frame/broker/src/tests.rs | 70 ++++++++++++++++++- substrate/frame/broker/src/tick_impls.rs | 6 ++ substrate/frame/broker/src/utility_impls.rs | 1 + 7 files changed, 89 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 1767245c6abf..fef1473919a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9800,6 +9800,7 @@ dependencies = [ "sp-io", "sp-runtime", "sp-std 14.0.0", + "sp-tracing 16.0.0", ] [[package]] diff --git a/substrate/frame/broker/Cargo.toml b/substrate/frame/broker/Cargo.toml index 8f3f30ec58eb..8a84fbfdfb70 100644 --- a/substrate/frame/broker/Cargo.toml +++ b/substrate/frame/broker/Cargo.toml @@ -30,6 +30,7 @@ frame-system = { path = "../system", default-features = false } [dev-dependencies] sp-io = { path = "../../primitives/io" } +sp-tracing = { path = "../../primitives/tracing" } pretty_assertions = "1.3.0" [features] diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 83c91270bdba..8800d8d21fb2 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -167,11 +167,18 @@ impl Pallet { let price_cap = record.price + config.renewal_bump * record.price; let now = frame_system::Pallet::::block_number(); let price = Self::sale_price(&sale, now).min(price_cap); + log::debug!( + "Renew with: sale price: {:?}, price cap: {:?}, old price: {:?}", + price, + price_cap, + record.price + ); let new_record = PotentialRenewalRecord { price, completion: Complete(workload) }; PotentialRenewals::::remove(renewal_id); PotentialRenewals::::insert(PotentialRenewalId { core, when: begin }, &new_record); SaleInfo::::put(&sale); if let Some(workload) = new_record.completion.drain_complete() { + log::debug!("Recording renewable price for next run: {:?}", price); Self::deposit_event(Event::Renewable { core, price, begin, workload }); } Ok(core) diff --git a/substrate/frame/broker/src/mock.rs b/substrate/frame/broker/src/mock.rs index da8943497ebc..6fff6aa10080 100644 --- a/substrate/frame/broker/src/mock.rs +++ b/substrate/frame/broker/src/mock.rs @@ -255,6 +255,10 @@ impl TestExt { Self(new_config()) } + pub fn new_with_config(config: ConfigRecordOf) -> Self { + Self(config) + } + pub fn advance_notice(mut self, advance_notice: Timeslice) -> Self { self.0.advance_notice = advance_notice as u64; self diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 18c6543d900b..3962b934f727 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -25,7 +25,7 @@ use frame_support::{ }; use frame_system::RawOrigin::Root; use pretty_assertions::assert_eq; -use sp_runtime::{traits::Get, TokenError}; +use sp_runtime::{traits::Get, Perbill, TokenError}; use CoreAssignment::*; use CoretimeTraceItem::*; use Finality::*; @@ -381,6 +381,74 @@ fn renewal_works() { }); } +#[test] +/// Renewals have to affect price as well. Otherwise a market where everything is a renewal would +/// not work. Renewals happening in the leadin or after are effectively competing with the open +/// market and it makes sense to adjust the price to what was paid here. Assuming all renewals were +/// done in the interlude and only normal sales happen in the leadin, renewals will have no effect +/// on price. If there are no cores left for sale on the open markent, renewals will affect price +/// even in the interlude, making sure renewal prices stay in the range of the open market. +fn renewals_affect_price() { + sp_tracing::try_init_simple(); + let b = 100_000; + let config = ConfigRecord { + advance_notice: 2, + interlude_length: 10, + leadin_length: 20, + ideal_bulk_proportion: Perbill::from_percent(100), + limit_cores_offered: None, + // Region length is in time slices (2 blocks): + region_length: 20, + renewal_bump: Perbill::from_percent(10), + contribution_timeout: 5, + }; + TestExt::new_with_config(config).endow(1, b).execute_with(|| { + let price = 910; + assert_ok!(Broker::do_start_sales(10, 1)); + advance_to(11); + let region = Broker::do_purchase(1, u64::max_value()).unwrap(); + // Price is lower, because already one block in: + let b = b - price; + assert_eq!(balance(1), b); + assert_ok!(Broker::do_assign(region, None, 1001, Final)); + advance_to(40); + assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::TooEarly); + let core = Broker::do_renew(1, region.core).unwrap(); + // First renewal has same price as initial purchase. + let b = b - price; + assert_eq!(balance(1), b); + advance_to(51); + assert_noop!(Broker::do_purchase(1, u64::max_value()), Error::::SoldOut); + advance_to(81); + assert_ok!(Broker::do_renew(1, core)); + // Renewal bump in effect + let price = price + Perbill::from_percent(10) * price; + let b = b - price; + assert_eq!(balance(1), b); + + // Move after interlude and leadin - should reduce price. + advance_to(159); + Broker::do_renew(1, region.core).unwrap(); + let price = price + Perbill::from_percent(10) * price; + let b = b - price; + assert_eq!(balance(1), b); + + advance_to(161); + // Should have the reduced price now: + Broker::do_renew(1, region.core).unwrap(); + let price = 100; + let b = b - price; + assert_eq!(balance(1), b); + + // Price should be bumped normally again: + advance_to(201); + Broker::do_renew(1, region.core).unwrap(); + let price = 110; + let b = b - price; + assert_eq!(balance(1), b); + }); +} + #[test] fn instapool_payouts_work() { TestExt::new().endow(1, 1000).execute_with(|| { diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 6743468a5b82..4e0fb64f77ff 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -162,6 +162,12 @@ impl Pallet { // Calculate the start price for the upcoming sale. let new_prices = T::PriceAdapter::adapt_price(SalePerformance::from_sale(&old_sale)); + log::debug!( + "Rotated sale, new prices: {:?}, {:?}", + new_prices.min_price, + new_prices.target_price + ); + // Set workload for the reserved (system, probably) workloads. let region_begin = old_sale.region_end; let region_end = region_begin + config.region_length; diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index 68bd74387234..f494bfab91dd 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -82,6 +82,7 @@ impl Pallet { sale: &mut SaleInfoRecordOf, ) -> Result { Self::charge(who, price)?; + log::debug!("Purchased core at: {:?}", price); let core = sale.first_core.saturating_add(sale.cores_sold); sale.cores_sold.saturating_inc(); if sale.cores_sold <= sale.ideal_cores_sold || sale.sellout_price.is_none() { From d5992b43f8dc73d2247efb03e1c9b88f6cdb2280 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 15:56:41 +0200 Subject: [PATCH 19/33] Fix prdoc --- prdoc/pr_4521.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index 6417c52b2417..a4f9894fa1ce 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -1,4 +1,4 @@ -title: Broker pallet: AdaptPrice price controller is now price controlled. +title: AdaptPrice trait is now price controlled doc: - audience: Runtime Dev From 03db4516e193270bceae46576bc6835a32755f24 Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 16:04:32 +0200 Subject: [PATCH 20/33] regular_price -> end_price --- substrate/frame/broker/src/benchmarking.rs | 4 ++-- substrate/frame/broker/src/lib.rs | 2 +- substrate/frame/broker/src/tick_impls.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index ca3c9ba7be98..500a1fbf9ec5 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -215,7 +215,7 @@ mod benches { sale_start: 2u32.into(), leadin_length: 1u32.into(), start_price: 20u32.into(), - regular_price: 10u32.into(), + end_price: 10u32.into(), region_begin: latest_region_begin + config.region_length, region_end: latest_region_begin + config.region_length * 2, ideal_cores_sold: 0, @@ -816,7 +816,7 @@ mod benches { sale_start: 2u32.into(), leadin_length: 1u32.into(), start_price: 20u32.into(), - regular_price: 10u32.into(), + end_price: 10u32.into(), region_begin: sale.region_begin + config.region_length, region_end: sale.region_end + config.region_length, ideal_cores_sold: 0, diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index f313544db56e..c6bcda63f581 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -292,7 +292,7 @@ pub mod pallet { /// The price of Bulk Coretime at the beginning of the Leadin Period. start_price: BalanceOf, /// The price of Bulk Coretime after the Leadin Period. - regular_price: BalanceOf, + end_price: BalanceOf, /// The first timeslice of the Regions which are being sold in this sale. region_begin: Timeslice, /// The timeslice on which the Regions which are being sold in the sale terminate. diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index 4e0fb64f77ff..da3d875f16bc 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -252,7 +252,7 @@ impl Pallet { sale_start, leadin_length, start_price: Self::sale_price(&new_sale, now), - regular_price: new_prices.min_price, + end_price: new_prices.min_price, region_begin, region_end, ideal_cores_sold, From d67dfc4aed1eac4cd09bb94c07e34cb6d451e1df Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 17:54:02 +0200 Subject: [PATCH 21/33] Fixes. --- .../runtimes/coretime/coretime-rococo/src/coretime.rs | 2 +- .../runtimes/coretime/coretime-westend/src/coretime.rs | 2 +- substrate/frame/broker/src/benchmarking.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs index ff78e49e3959..ec3a4f31202f 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/coretime.rs @@ -232,5 +232,5 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::CenterTargetPrice; + type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs index 97bee8afb88f..a5e219b9897e 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/coretime.rs @@ -245,5 +245,5 @@ impl pallet_broker::Config for Runtime { type WeightInfo = weights::pallet_broker::WeightInfo; type PalletId = BrokerPalletId; type AdminOrigin = EnsureRoot; - type PriceAdapter = pallet_broker::CenterTargetPrice; + type PriceAdapter = pallet_broker::CenterTargetPrice; } diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 500a1fbf9ec5..fae29a5cb3ce 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -776,12 +776,12 @@ mod benches { let config = new_config_record::(); let now = frame_system::Pallet::::block_number(); - let price = 10u32.into(); + let min_price = 10u32.into(); let commit_timeslice = Broker::::latest_timeslice_ready_to_commit(&config); let sale = SaleInfoRecordOf:: { sale_start: now, leadin_length: Zero::zero(), - price, + min_price, sellout_price: None, region_begin: commit_timeslice, region_end: commit_timeslice.saturating_add(config.region_length), From 890e6ca4fa05592b1bdaebe0dd89bcc8ed9dde0e Mon Sep 17 00:00:00 2001 From: eskimor Date: Mon, 27 May 2024 18:49:30 +0200 Subject: [PATCH 22/33] Fix benchmarks --- substrate/frame/broker/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index fae29a5cb3ce..18f0e26fb15a 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -214,7 +214,7 @@ mod benches { Event::SaleInitialized { sale_start: 2u32.into(), leadin_length: 1u32.into(), - start_price: 20u32.into(), + start_price: 1000u32.into(), end_price: 10u32.into(), region_begin: latest_region_begin + config.region_length, region_end: latest_region_begin + config.region_length * 2, @@ -815,7 +815,7 @@ mod benches { Event::SaleInitialized { sale_start: 2u32.into(), leadin_length: 1u32.into(), - start_price: 20u32.into(), + start_price: 1000u32.into(), end_price: 10u32.into(), region_begin: sale.region_begin + config.region_length, region_end: sale.region_end + config.region_length, From 66bce5a10d252dedcb1cb3f4d89c95122211c5f3 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 10:16:53 +0200 Subject: [PATCH 23/33] Update prdoc/pr_4521.prdoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dónal Murray --- prdoc/pr_4521.prdoc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index a4f9894fa1ce..966e51e862f0 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -3,10 +3,11 @@ title: AdaptPrice trait is now price controlled doc: - audience: Runtime Dev description: | - We discovered a flaw of the current price controller interface in the - broker pallet. This is fixed by changing the interface to no longer - operate on the number of cores sold, but rather on the price that was - achieved during the sale. More information here: + The broker pallet price adaptation interface is changed to be less opinionated and more + information is made available to the `AdaptPrice` trait. A new example impl is included which + adapts the price based not on the number of cores sold, but rather on the price that was + achieved during the sale to mitigate a potential price manipulation vector. More information + here: https://github.com/paritytech/polkadot-sdk/issues/4360 From fbe52414cbe075ad7ae7df9e69b11d0ffaf05a38 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 10:40:11 +0200 Subject: [PATCH 24/33] min_price -> end_price --- substrate/frame/broker/src/adapt_price.rs | 40 +++++++++---------- substrate/frame/broker/src/benchmarking.rs | 4 +- .../frame/broker/src/dispatchable_impls.rs | 6 +-- substrate/frame/broker/src/lib.rs | 6 +-- substrate/frame/broker/src/tests.rs | 4 +- substrate/frame/broker/src/tick_impls.rs | 8 ++-- substrate/frame/broker/src/types.rs | 2 +- substrate/frame/broker/src/utility_impls.rs | 2 +- 8 files changed, 36 insertions(+), 36 deletions(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index 774318d1c579..b2f802ea3c67 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -30,7 +30,7 @@ pub struct SalePerformance { pub sellout_price: Option, /// The minimum price that was achieved in this sale. - pub min_price: Balance, + pub end_price: Balance, /// The number of cores we want to sell, ideally. pub ideal_cores_sold: CoreIndex, @@ -46,7 +46,7 @@ pub struct SalePerformance { #[derive(Copy, Clone)] pub struct AdaptedPrices { /// New minimum price to use. - pub min_price: Balance, + pub end_price: Balance, /// Price we optimize for. pub target_price: Balance, } @@ -56,7 +56,7 @@ impl SalePerformance { pub fn from_sale(record: &SaleInfoRecord) -> Self { Self { sellout_price: record.sellout_price, - min_price: record.min_price, + end_price: record.end_price, ideal_cores_sold: record.ideal_cores_sold, cores_offered: record.cores_offered, cores_sold: record.cores_sold, @@ -64,8 +64,8 @@ impl SalePerformance { } #[cfg(test)] - fn new(sellout_price: Option, min_price: Balance) -> Self { - Self { sellout_price, min_price, ideal_cores_sold: 0, cores_offered: 0, cores_sold: 0 } + fn new(sellout_price: Option, end_price: Balance) -> Self { + Self { sellout_price, end_price, ideal_cores_sold: 0, cores_offered: 0, cores_sold: 0 } } } @@ -87,8 +87,8 @@ impl AdaptPrice for () { FixedU64::one() } fn adapt_price(performance: SalePerformance) -> AdaptedPrices { - let price = performance.sellout_price.unwrap_or(performance.min_price); - AdaptedPrices { min_price: price, target_price: price } + let price = performance.sellout_price.unwrap_or(performance.end_price); + AdaptedPrices { end_price: price, target_price: price } } } @@ -110,8 +110,8 @@ impl AdaptPrice for CenterTargetPrice) -> AdaptedPrices { let Some(sellout_price) = performance.sellout_price else { return AdaptedPrices { - min_price: performance.min_price, - target_price: FixedU64::from(10).saturating_mul_int(performance.min_price), + end_price: performance.end_price, + target_price: FixedU64::from(10).saturating_mul_int(performance.end_price), } }; @@ -123,7 +123,7 @@ impl AdaptPrice for CenterTargetPrice= 99); + assert!(prices.end_price <= 101); + assert!(prices.end_price >= 99); assert!(prices.target_price <= 1001); assert!(prices.target_price >= 999); } @@ -169,7 +169,7 @@ mod tests { let performance = SalePerformance::new(Some(10_000), 100); let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 10_000); - assert_eq!(prices.min_price, 1000); + assert_eq!(prices.end_price, 1000); } #[test] @@ -177,7 +177,7 @@ mod tests { let performance = SalePerformance::new(Some(100), 100); let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 100); - assert_eq!(prices.min_price, 10); + assert_eq!(prices.end_price, 10); } #[test] @@ -188,10 +188,10 @@ mod tests { for _ in 0..11 { let prices = CenterTargetPrice::adapt_price(performance); performance.sellout_price = Some(sellout_price); - performance.min_price = prices.min_price; + performance.end_price = prices.end_price; - assert!(prices.min_price <= sellout_price); - assert!(prices.min_price > 0); + assert!(prices.end_price <= sellout_price); + assert!(prices.end_price > 0); } } @@ -200,7 +200,7 @@ mod tests { let performance = SalePerformance::new(None, 100); let prices = CenterTargetPrice::adapt_price(performance); assert_eq!(prices.target_price, 1000); - assert_eq!(prices.min_price, 100); + assert_eq!(prices.end_price, 100); } #[test] diff --git a/substrate/frame/broker/src/benchmarking.rs b/substrate/frame/broker/src/benchmarking.rs index 18f0e26fb15a..9cb5ad096c83 100644 --- a/substrate/frame/broker/src/benchmarking.rs +++ b/substrate/frame/broker/src/benchmarking.rs @@ -776,12 +776,12 @@ mod benches { let config = new_config_record::(); let now = frame_system::Pallet::::block_number(); - let min_price = 10u32.into(); + let end_price = 10u32.into(); let commit_timeslice = Broker::::latest_timeslice_ready_to_commit(&config); let sale = SaleInfoRecordOf:: { sale_start: now, leadin_length: Zero::zero(), - min_price, + end_price, sellout_price: None, region_begin: commit_timeslice, region_end: commit_timeslice.saturating_add(config.region_length), diff --git a/substrate/frame/broker/src/dispatchable_impls.rs b/substrate/frame/broker/src/dispatchable_impls.rs index 8800d8d21fb2..79c1a1f79796 100644 --- a/substrate/frame/broker/src/dispatchable_impls.rs +++ b/substrate/frame/broker/src/dispatchable_impls.rs @@ -71,7 +71,7 @@ impl Pallet { } pub(crate) fn do_start_sales( - min_price: BalanceOf, + end_price: BalanceOf, extra_cores: CoreIndex, ) -> DispatchResult { let config = Configuration::::get().ok_or(Error::::Uninitialized)?; @@ -96,7 +96,7 @@ impl Pallet { let old_sale = SaleInfoRecord { sale_start: now, leadin_length: Zero::zero(), - min_price, + end_price, sellout_price: None, region_begin: commit_timeslice, region_end: commit_timeslice.saturating_add(config.region_length), @@ -105,7 +105,7 @@ impl Pallet { cores_offered: 0, cores_sold: 0, }; - Self::deposit_event(Event::::SalesStarted { price: min_price, core_count }); + Self::deposit_event(Event::::SalesStarted { price: end_price, core_count }); Self::rotate_sale(old_sale, &config, &status); Status::::put(&status); Ok(()) diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index c6bcda63f581..807ac2e97d2d 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -559,7 +559,7 @@ pub mod pallet { /// Begin the Bulk Coretime sales rotation. /// /// - `origin`: Must be Root or pass `AdminOrigin`. - /// - `min_price`: The price after the leadin period of Bulk Coretime in the first sale. + /// - `end_price`: The price after the leadin period of Bulk Coretime in the first sale. /// - `extra_cores`: Number of extra cores that should be requested on top of the cores /// required for `Reservations` and `Leases`. /// @@ -571,11 +571,11 @@ pub mod pallet { ))] pub fn start_sales( origin: OriginFor, - min_price: BalanceOf, + end_price: BalanceOf, extra_cores: CoreIndex, ) -> DispatchResultWithPostInfo { T::AdminOrigin::ensure_origin_or_root(origin)?; - Self::do_start_sales(min_price, extra_cores)?; + Self::do_start_sales(end_price, extra_cores)?; Ok(Pays::No.into()) } diff --git a/substrate/frame/broker/src/tests.rs b/substrate/frame/broker/src/tests.rs index 3962b934f727..e953afd6dc3c 100644 --- a/substrate/frame/broker/src/tests.rs +++ b/substrate/frame/broker/src/tests.rs @@ -1172,7 +1172,7 @@ fn purchase_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - min_price: 200, + end_price: 200, sellout_price: None, region_begin: 0, region_end: 3, @@ -1214,7 +1214,7 @@ fn renewal_requires_valid_status_and_sale_info() { let mut dummy_sale = SaleInfoRecord { sale_start: 0, leadin_length: 0, - min_price: 200, + end_price: 200, sellout_price: None, region_begin: 0, region_end: 3, diff --git a/substrate/frame/broker/src/tick_impls.rs b/substrate/frame/broker/src/tick_impls.rs index da3d875f16bc..20637cf7b903 100644 --- a/substrate/frame/broker/src/tick_impls.rs +++ b/substrate/frame/broker/src/tick_impls.rs @@ -164,7 +164,7 @@ impl Pallet { log::debug!( "Rotated sale, new prices: {:?}, {:?}", - new_prices.min_price, + new_prices.end_price, new_prices.target_price ); @@ -228,7 +228,7 @@ impl Pallet { let ideal_cores_sold = (config.ideal_bulk_proportion * cores_offered as u32) as u16; let sellout_price = if cores_offered > 0 { // No core sold -> price was too high -> we have to adjust downwards. - Some(new_prices.min_price) + Some(new_prices.end_price) } else { None }; @@ -237,7 +237,7 @@ impl Pallet { let new_sale = SaleInfoRecord { sale_start, leadin_length, - min_price: new_prices.min_price, + end_price: new_prices.end_price, sellout_price, region_begin, region_end, @@ -252,7 +252,7 @@ impl Pallet { sale_start, leadin_length, start_price: Self::sale_price(&new_sale, now), - end_price: new_prices.min_price, + end_price: new_prices.end_price, region_begin, region_end, ideal_cores_sold, diff --git a/substrate/frame/broker/src/types.rs b/substrate/frame/broker/src/types.rs index 7c9b5b85055f..885cac9a5c23 100644 --- a/substrate/frame/broker/src/types.rs +++ b/substrate/frame/broker/src/types.rs @@ -214,7 +214,7 @@ pub struct SaleInfoRecord { /// The length in blocks of the Leadin Period (where the price is decreasing). pub leadin_length: BlockNumber, /// The price of Bulk Coretime after the Leadin Period. - pub min_price: Balance, + pub end_price: Balance, /// The first timeslice of the Regions which are being sold in this sale. pub region_begin: Timeslice, /// The timeslice on which the Regions which are being sold in the sale terminate. (i.e. One diff --git a/substrate/frame/broker/src/utility_impls.rs b/substrate/frame/broker/src/utility_impls.rs index f494bfab91dd..9cceb7f970a9 100644 --- a/substrate/frame/broker/src/utility_impls.rs +++ b/substrate/frame/broker/src/utility_impls.rs @@ -63,7 +63,7 @@ impl Pallet { pub fn sale_price(sale: &SaleInfoRecordOf, now: BlockNumberFor) -> BalanceOf { let num = now.saturating_sub(sale.sale_start).min(sale.leadin_length).saturated_into(); let through = FixedU64::from_rational(num, sale.leadin_length.saturated_into()); - T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.min_price) + T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.end_price) } pub(crate) fn charge(who: &T::AccountId, amount: BalanceOf) -> DispatchResult { From fdf764e38f4407a8b1c9911e786f264f472d49e1 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 10:46:52 +0200 Subject: [PATCH 25/33] Better docs for target_price. --- substrate/frame/broker/src/adapt_price.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index b2f802ea3c67..d598580abf0f 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -47,7 +47,15 @@ pub struct SalePerformance { pub struct AdaptedPrices { /// New minimum price to use. pub end_price: Balance, - /// Price we optimize for. + + /// Price the controller is optimizing for. + /// + /// This is the price "expected" by the controller based on the previous sale. We assume that + /// sales in this period will be around this price, assuming stable market conditions. + /// + /// Think of it as the expected market price. This can be used for determining what to charge + /// for renewals, that don't yet have any price information for example. E.g. for expired + /// legacy leases. pub target_price: Balance, } From 86ec07c3c93d2a82a3d997125290c306dd58a212 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 10:48:36 +0200 Subject: [PATCH 26/33] Improved prdoc --- prdoc/pr_4521.prdoc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index a4f9894fa1ce..d0e002bb7ad9 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -12,12 +12,12 @@ doc: - audience: Runtime User description: | - The price controller of the Coretime chain will be adjusted with this - release. This will very likely be used in the fellowship production - runtime to have a much larger leadin. This fixes a price manipulation - issue we discovered with the Kusama launch. + The price controller of the Rococo and Westend Coretime chain will be + adjusted with this release. This will very likely be used in the + fellowship production runtime to have a much larger leadin. This fixes a + price manipulation issue we discovered with the Kusama launch. crates: - name: pallet-broker - bump: major + bump: minor From 2a940a5761dbfd1899e307092a29832130de39a9 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 11:06:20 +0200 Subject: [PATCH 27/33] Make change major again. --- prdoc/pr_4521.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index 02fbe5566ccb..03e06428ce2c 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -20,5 +20,5 @@ doc: crates: - name: pallet-broker - bump: minor + bump: major From 52e0b80bf278386e5b63b09e8dcb20cdc802f3a0 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 12:14:42 +0200 Subject: [PATCH 28/33] One more test. --- substrate/frame/broker/src/adapt_price.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/substrate/frame/broker/src/adapt_price.rs b/substrate/frame/broker/src/adapt_price.rs index d598580abf0f..9b2e1dd8997b 100644 --- a/substrate/frame/broker/src/adapt_price.rs +++ b/substrate/frame/broker/src/adapt_price.rs @@ -149,6 +149,29 @@ mod tests { } } + #[test] + fn leadin_price_bound_check() { + assert_eq!( + CenterTargetPrice::::leadin_factor_at(FixedU64::from(0)), + FixedU64::from(100) + ); + assert_eq!( + CenterTargetPrice::::leadin_factor_at(FixedU64::from_rational(1, 4)), + FixedU64::from(55) + ); + + assert_eq!( + CenterTargetPrice::::leadin_factor_at(FixedU64::from_float(0.5)), + FixedU64::from(10) + ); + + assert_eq!( + CenterTargetPrice::::leadin_factor_at(FixedU64::from_rational(3, 4)), + FixedU64::from_float(5.5) + ); + assert_eq!(CenterTargetPrice::::leadin_factor_at(FixedU64::one()), FixedU64::one()); + } + #[test] fn no_op_sale_is_good() { let prices = CenterTargetPrice::adapt_price(SalePerformance::new(None, 1)); From 812f66cbff70ba2e11b40ea9ce8c574ec1e0d3e1 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 12:22:41 +0200 Subject: [PATCH 29/33] Add migration for name change `PotentialRenewals`. --- .../coretime/coretime-rococo/src/lib.rs | 1 + .../coretime/coretime-westend/src/lib.rs | 1 + substrate/frame/broker/src/lib.rs | 2 +- substrate/frame/broker/src/migration.rs | 59 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs index f43bb1c1e41b..b78802790480 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs @@ -112,6 +112,7 @@ pub type Migrations = ( cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, cumulus_pallet_xcmp_queue::migration::v5::MigrateV4ToV5, pallet_broker::migration::MigrateV0ToV1, + pallet_broker::migration::MigrateV1ToV2, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); diff --git a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs index ff2456dc1772..78b963e3b405 100644 --- a/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/coretime/coretime-westend/src/lib.rs @@ -111,6 +111,7 @@ pub type Migrations = ( pallet_collator_selection::migration::v2::MigrationToV2, cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4, pallet_broker::migration::MigrateV0ToV1, + pallet_broker::migration::MigrateV1ToV2, // permanent pallet_xcm::migration::MigrateToLatestXcmVersion, ); diff --git a/substrate/frame/broker/src/lib.rs b/substrate/frame/broker/src/lib.rs index 807ac2e97d2d..0774c02e1cf1 100644 --- a/substrate/frame/broker/src/lib.rs +++ b/substrate/frame/broker/src/lib.rs @@ -65,7 +65,7 @@ pub mod pallet { use sp_runtime::traits::{Convert, ConvertBack}; use sp_std::vec::Vec; - const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + const STORAGE_VERSION: StorageVersion = StorageVersion::new(2); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] diff --git a/substrate/frame/broker/src/migration.rs b/substrate/frame/broker/src/migration.rs index 95aa28250a62..f354e447fe84 100644 --- a/substrate/frame/broker/src/migration.rs +++ b/substrate/frame/broker/src/migration.rs @@ -77,6 +77,57 @@ mod v1 { } } +mod v2 { + use super::*; + use frame_support::{ + pallet_prelude::{OptionQuery, Twox64Concat}, + storage_alias, + }; + + #[storage_alias] + pub type AllowedRenewals = StorageMap< + Pallet, + Twox64Concat, + PotentialRenewalId, + PotentialRenewalRecordOf, + OptionQuery, + >; + + pub struct MigrateToV2Impl(PhantomData); + + impl UncheckedOnRuntimeUpgrade for MigrateToV2Impl { + fn on_runtime_upgrade() -> frame_support::weights::Weight { + let mut count = 0; + for (renewal_id, renewal) in AllowedRenewals::::drain() { + PotentialRenewals::::insert(renewal_id, renewal); + count += 1; + } + + log::info!( + target: LOG_TARGET, + "Storage migration v2 for pallet-broker finished.", + ); + + // calculate and return migration weights + T::DbWeight::get().reads_writes(count as u64 + 1, count as u64 + 1) + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, sp_runtime::TryRuntimeError> { + Ok((AllowedRenewals::::iter_keys().count() as u32).encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec) -> Result<(), sp_runtime::TryRuntimeError> { + let old_count = u32::decode(&mut &state[..]).expect("Known good"); + let new_count = PotentialRenewals::::iter_values().count() as u32; + + ensure!(old_count == new_count, "Renewal count should not change"); + Ok(()) + } + } +} + /// Migrate the pallet storage from `0` to `1`. pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< 0, @@ -85,3 +136,11 @@ pub type MigrateV0ToV1 = frame_support::migrations::VersionedMigration< Pallet, ::DbWeight, >; + +pub type MigrateV1ToV2 = frame_support::migrations::VersionedMigration< + 1, + 2, + v2::MigrateToV2Impl, + Pallet, + ::DbWeight, +>; From 7e2992c3caefb3867672a310261db9525b2f1edd Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 12:33:04 +0200 Subject: [PATCH 30/33] Fix prdoc? --- prdoc/pr_4521.prdoc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index 03e06428ce2c..fbe1fcdac363 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -3,13 +3,13 @@ title: AdaptPrice trait is now price controlled doc: - audience: Runtime Dev description: | - The broker pallet price adaptation interface is changed to be less opinionated and more - information is made available to the `AdaptPrice` trait. A new example impl is included which - adapts the price based not on the number of cores sold, but rather on the price that was - achieved during the sale to mitigate a potential price manipulation vector. More information - here: + The broker pallet price adaptation interface is changed to be less opinionated and more + information is made available to the `AdaptPrice` trait. A new example impl is included which + adapts the price based not on the number of cores sold, but rather on the price that was + achieved during the sale to mitigate a potential price manipulation vector. More information + here: - https://github.com/paritytech/polkadot-sdk/issues/4360 + https://github.com/paritytech/polkadot-sdk/issues/4360 - audience: Runtime User description: | From 4cc238a7e2451f7976be8e9d7a37a95bc4d2a34f Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Wed, 29 May 2024 11:53:59 +0000 Subject: [PATCH 31/33] ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=dev --target_dir=substrate --pallet=pallet_broker --- substrate/frame/broker/src/weights.rs | 372 +++++++++++++------------- 1 file changed, 181 insertions(+), 191 deletions(-) diff --git a/substrate/frame/broker/src/weights.rs b/substrate/frame/broker/src/weights.rs index 234a64fc3521..d9d9d348e47e 100644 --- a/substrate/frame/broker/src/weights.rs +++ b/substrate/frame/broker/src/weights.rs @@ -18,27 +18,25 @@ //! Autogenerated weights for `pallet_broker` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2024-04-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-05-29, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `runner-anb7yjbi-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `runner-vicqj8em-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/production/substrate-node +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_broker -// --no-storage-info -// --no-median-slopes -// --no-min-squares // --extrinsic=* // --wasm-execution=compiled // --heap-pages=4096 -// --output=./substrate/frame/broker/src/weights.rs +// --json-file=/builds/parity/mirrors/polkadot-sdk/.git/.artifacts/bench.json +// --pallet=pallet_broker +// --chain=dev // --header=./substrate/HEADER-APACHE2 +// --output=./substrate/frame/broker/src/weights.rs // --template=./substrate/.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] @@ -90,8 +88,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_624_000 picoseconds. - Weight::from_parts(2_804_000, 0) + // Minimum execution time: 1_945_000 picoseconds. + Weight::from_parts(2_142_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -100,8 +98,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 18_451_000 picoseconds. - Weight::from_parts(18_853_000, 7496) + // Minimum execution time: 16_274_000 picoseconds. + Weight::from_parts(16_828_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -111,8 +109,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 16_899_000 picoseconds. - Weight::from_parts(17_645_000, 7496) + // Minimum execution time: 15_080_000 picoseconds. + Weight::from_parts(15_874_000, 7496) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -122,19 +120,19 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 10_239_000 picoseconds. - Weight::from_parts(10_754_000, 1526) + // Minimum execution time: 8_761_000 picoseconds. + Weight::from_parts(9_203_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: `Broker::InstaPoolIo` (r:3 w:3) - /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Broker::Reservations` (r:1 w:0) - /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) /// Storage: `Broker::Leases` (r:1 w:1) /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + /// Storage: `Broker::Reservations` (r:1 w:0) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolIo` (r:3 w:3) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:0 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) /// Storage: `Broker::Status` (r:0 w:1) @@ -146,12 +144,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 51_250_000 picoseconds. - Weight::from_parts(54_643_012, 8499) - // Standard Error: 147 - .saturating_add(Weight::from_parts(18, 0).saturating_mul(n.into())) + // Minimum execution time: 26_057_000 picoseconds. + Weight::from_parts(46_673_357, 8499) + // Standard Error: 456 + .saturating_add(Weight::from_parts(2_677, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(16_u64)) + .saturating_add(T::DbWeight::get().writes(15_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) @@ -162,13 +160,13 @@ impl WeightInfo for SubstrateWeight { /// Storage: `System::Digest` (r:1 w:0) /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Broker::Regions` (r:0 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `635` - // Estimated: `2120` - // Minimum execution time: 43_660_000 picoseconds. - Weight::from_parts(45_543_000, 2120) + // Measured: `651` + // Estimated: `2136` + // Minimum execution time: 40_907_000 picoseconds. + Weight::from_parts(42_566_000, 2136) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -188,43 +186,43 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `753` + // Measured: `769` // Estimated: `4698` - // Minimum execution time: 63_122_000 picoseconds. - Weight::from_parts(64_366_000, 4698) + // Minimum execution time: 65_209_000 picoseconds. + Weight::from_parts(68_604_000, 4698) .saturating_add(T::DbWeight::get().reads(6_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 17_552_000 picoseconds. - Weight::from_parts(18_251_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 15_860_000 picoseconds. + Weight::from_parts(16_393_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Regions` (r:1 w:2) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn partition() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 18_551_000 picoseconds. - Weight::from_parts(19_727_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 17_651_000 picoseconds. + Weight::from_parts(18_088_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Broker::Regions` (r:1 w:3) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 20_636_000 picoseconds. - Weight::from_parts(21_060_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 18_576_000 picoseconds. + Weight::from_parts(19_810_000, 3551) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -233,22 +231,22 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:1 w:1) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn assign() -> Weight { // Proof Size summary in bytes: - // Measured: `740` + // Measured: `741` // Estimated: `4681` - // Minimum execution time: 32_394_000 picoseconds. - Weight::from_parts(33_324_000, 4681) + // Minimum execution time: 31_015_000 picoseconds. + Weight::from_parts(31_932_000, 4681) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:1 w:1) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// Storage: `Broker::InstaPoolIo` (r:2 w:2) @@ -257,10 +255,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Broker::InstaPoolContribution` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn pool() -> Weight { // Proof Size summary in bytes: - // Measured: `775` + // Measured: `776` // Estimated: `5996` - // Minimum execution time: 38_128_000 picoseconds. - Weight::from_parts(39_274_000, 5996) + // Minimum execution time: 36_473_000 picoseconds. + Weight::from_parts(37_382_000, 5996) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(5_u64)) } @@ -275,10 +273,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 70_453_000 picoseconds. - Weight::from_parts(70_652_822, 6196) - // Standard Error: 75_524 - .saturating_add(Weight::from_parts(2_335_289, 0).saturating_mul(m.into())) + // Minimum execution time: 64_957_000 picoseconds. + Weight::from_parts(66_024_232, 6196) + // Standard Error: 50_170 + .saturating_add(Weight::from_parts(1_290_632, 0).saturating_mul(m.into())) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(T::DbWeight::get().writes(5_u64)) @@ -290,21 +288,21 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 43_945_000 picoseconds. - Weight::from_parts(45_249_000, 3593) + // Minimum execution time: 39_939_000 picoseconds. + Weight::from_parts(40_788_000, 3593) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn drop_region() -> Weight { // Proof Size summary in bytes: - // Measured: `603` - // Estimated: `3550` - // Minimum execution time: 30_680_000 picoseconds. - Weight::from_parts(32_995_000, 3550) + // Measured: `604` + // Estimated: `3551` + // Minimum execution time: 31_709_000 picoseconds. + Weight::from_parts(37_559_000, 3551) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -318,8 +316,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 48_053_000 picoseconds. - Weight::from_parts(51_364_000, 3533) + // Minimum execution time: 42_895_000 picoseconds. + Weight::from_parts(53_945_000, 3533) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -335,8 +333,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `995` // Estimated: `3593` - // Minimum execution time: 57_372_000 picoseconds. - Weight::from_parts(59_466_000, 3593) + // Minimum execution time: 50_770_000 picoseconds. + Weight::from_parts(63_117_000, 3593) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -348,8 +346,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4698` - // Minimum execution time: 27_768_000 picoseconds. - Weight::from_parts(29_000_000, 4698) + // Minimum execution time: 33_396_000 picoseconds. + Weight::from_parts(36_247_000, 4698) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -358,20 +356,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_588_000 picoseconds. - Weight::from_parts(5_201_705, 0) + // Minimum execution time: 3_625_000 picoseconds. + Weight::from_parts(4_011_396, 0) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn process_core_count(n: u32, ) -> Weight { + fn process_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `404` // Estimated: `1487` - // Minimum execution time: 6_889_000 picoseconds. - Weight::from_parts(7_380_363, 1487) - // Standard Error: 21 - .saturating_add(Weight::from_parts(63, 0).saturating_mul(n.into())) + // Minimum execution time: 6_217_000 picoseconds. + Weight::from_parts(6_608_394, 1487) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -389,8 +385,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `972` // Estimated: `4437` - // Minimum execution time: 50_156_000 picoseconds. - Weight::from_parts(51_610_000, 4437) + // Minimum execution time: 46_853_000 picoseconds. + Weight::from_parts(47_740_000, 4437) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -405,14 +401,12 @@ impl WeightInfo for SubstrateWeight { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(n: u32, ) -> Weight { + fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 38_246_000 picoseconds. - Weight::from_parts(40_008_850, 8499) - // Standard Error: 94 - .saturating_add(Weight::from_parts(964, 0).saturating_mul(n.into())) + // Minimum execution time: 34_240_000 picoseconds. + Weight::from_parts(35_910_175, 8499) .saturating_add(T::DbWeight::get().reads(5_u64)) .saturating_add(T::DbWeight::get().writes(15_u64)) } @@ -424,8 +418,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 7_962_000 picoseconds. - Weight::from_parts(8_313_000, 3493) + // Minimum execution time: 7_083_000 picoseconds. + Weight::from_parts(7_336_000, 3493) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -437,8 +431,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 17_457_000 picoseconds. - Weight::from_parts(18_387_000, 4681) + // Minimum execution time: 15_029_000 picoseconds. + Weight::from_parts(15_567_000, 4681) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -446,8 +440,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 133_000 picoseconds. - Weight::from_parts(149_000, 0) + // Minimum execution time: 123_000 picoseconds. + Weight::from_parts(136_000, 0) } /// Storage: `Broker::CoreCountInbox` (r:0 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -455,8 +449,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_407_000 picoseconds. - Weight::from_parts(2_634_000, 0) + // Minimum execution time: 1_775_000 picoseconds. + Weight::from_parts(1_911_000, 0) .saturating_add(T::DbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) @@ -471,8 +465,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `603` // Estimated: `4068` - // Minimum execution time: 13_043_000 picoseconds. - Weight::from_parts(13_541_000, 4068) + // Minimum execution time: 11_859_000 picoseconds. + Weight::from_parts(12_214_000, 4068) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -482,8 +476,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 6_606_000 picoseconds. - Weight::from_parts(6_964_000, 1526) + // Minimum execution time: 5_864_000 picoseconds. + Weight::from_parts(6_231_000, 1526) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -497,8 +491,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_624_000 picoseconds. - Weight::from_parts(2_804_000, 0) + // Minimum execution time: 1_945_000 picoseconds. + Weight::from_parts(2_142_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Reservations` (r:1 w:1) @@ -507,8 +501,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `5016` // Estimated: `7496` - // Minimum execution time: 18_451_000 picoseconds. - Weight::from_parts(18_853_000, 7496) + // Minimum execution time: 16_274_000 picoseconds. + Weight::from_parts(16_828_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -518,8 +512,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6218` // Estimated: `7496` - // Minimum execution time: 16_899_000 picoseconds. - Weight::from_parts(17_645_000, 7496) + // Minimum execution time: 15_080_000 picoseconds. + Weight::from_parts(15_874_000, 7496) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -529,19 +523,19 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 10_239_000 picoseconds. - Weight::from_parts(10_754_000, 1526) + // Minimum execution time: 8_761_000 picoseconds. + Weight::from_parts(9_203_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Configuration` (r:1 w:0) /// Proof: `Broker::Configuration` (`max_values`: Some(1), `max_size`: Some(31), added: 526, mode: `MaxEncodedLen`) - /// Storage: `Broker::InstaPoolIo` (r:3 w:3) - /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) - /// Storage: `Broker::Reservations` (r:1 w:0) - /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) /// Storage: `Broker::Leases` (r:1 w:1) /// Proof: `Broker::Leases` (`max_values`: Some(1), `max_size`: Some(41), added: 536, mode: `MaxEncodedLen`) + /// Storage: `Broker::Reservations` (r:1 w:0) + /// Proof: `Broker::Reservations` (`max_values`: Some(1), `max_size`: Some(6011), added: 6506, mode: `MaxEncodedLen`) + /// Storage: `Broker::InstaPoolIo` (r:3 w:3) + /// Proof: `Broker::InstaPoolIo` (`max_values`: None, `max_size`: Some(28), added: 2503, mode: `MaxEncodedLen`) /// Storage: `Broker::SaleInfo` (r:0 w:1) /// Proof: `Broker::SaleInfo` (`max_values`: Some(1), `max_size`: Some(57), added: 552, mode: `MaxEncodedLen`) /// Storage: `Broker::Status` (r:0 w:1) @@ -553,12 +547,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `6330` // Estimated: `8499` - // Minimum execution time: 51_250_000 picoseconds. - Weight::from_parts(54_643_012, 8499) - // Standard Error: 147 - .saturating_add(Weight::from_parts(18, 0).saturating_mul(n.into())) + // Minimum execution time: 26_057_000 picoseconds. + Weight::from_parts(46_673_357, 8499) + // Standard Error: 456 + .saturating_add(Weight::from_parts(2_677, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(16_u64)) + .saturating_add(RocksDbWeight::get().writes(15_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) @@ -569,13 +563,13 @@ impl WeightInfo for () { /// Storage: `System::Digest` (r:1 w:0) /// Proof: `System::Digest` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) /// Storage: `Broker::Regions` (r:0 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn purchase() -> Weight { // Proof Size summary in bytes: - // Measured: `635` - // Estimated: `2120` - // Minimum execution time: 43_660_000 picoseconds. - Weight::from_parts(45_543_000, 2120) + // Measured: `651` + // Estimated: `2136` + // Minimum execution time: 40_907_000 picoseconds. + Weight::from_parts(42_566_000, 2136) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -595,43 +589,43 @@ impl WeightInfo for () { /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn renew() -> Weight { // Proof Size summary in bytes: - // Measured: `753` + // Measured: `769` // Estimated: `4698` - // Minimum execution time: 63_122_000 picoseconds. - Weight::from_parts(64_366_000, 4698) + // Minimum execution time: 65_209_000 picoseconds. + Weight::from_parts(68_604_000, 4698) .saturating_add(RocksDbWeight::get().reads(6_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn transfer() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 17_552_000 picoseconds. - Weight::from_parts(18_251_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 15_860_000 picoseconds. + Weight::from_parts(16_393_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Regions` (r:1 w:2) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn partition() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 18_551_000 picoseconds. - Weight::from_parts(19_727_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 17_651_000 picoseconds. + Weight::from_parts(18_088_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Broker::Regions` (r:1 w:3) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn interlace() -> Weight { // Proof Size summary in bytes: - // Measured: `495` - // Estimated: `3550` - // Minimum execution time: 20_636_000 picoseconds. - Weight::from_parts(21_060_000, 3550) + // Measured: `496` + // Estimated: `3551` + // Minimum execution time: 18_576_000 picoseconds. + Weight::from_parts(19_810_000, 3551) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -640,22 +634,22 @@ impl WeightInfo for () { /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:1 w:1) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) fn assign() -> Weight { // Proof Size summary in bytes: - // Measured: `740` + // Measured: `741` // Estimated: `4681` - // Minimum execution time: 32_394_000 picoseconds. - Weight::from_parts(33_324_000, 4681) + // Minimum execution time: 31_015_000 picoseconds. + Weight::from_parts(31_932_000, 4681) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) /// Storage: `Broker::Workplan` (r:1 w:1) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// Storage: `Broker::InstaPoolIo` (r:2 w:2) @@ -664,10 +658,10 @@ impl WeightInfo for () { /// Proof: `Broker::InstaPoolContribution` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn pool() -> Weight { // Proof Size summary in bytes: - // Measured: `775` + // Measured: `776` // Estimated: `5996` - // Minimum execution time: 38_128_000 picoseconds. - Weight::from_parts(39_274_000, 5996) + // Minimum execution time: 36_473_000 picoseconds. + Weight::from_parts(37_382_000, 5996) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(5_u64)) } @@ -682,10 +676,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `859` // Estimated: `6196 + m * (2520 ±0)` - // Minimum execution time: 70_453_000 picoseconds. - Weight::from_parts(70_652_822, 6196) - // Standard Error: 75_524 - .saturating_add(Weight::from_parts(2_335_289, 0).saturating_mul(m.into())) + // Minimum execution time: 64_957_000 picoseconds. + Weight::from_parts(66_024_232, 6196) + // Standard Error: 50_170 + .saturating_add(Weight::from_parts(1_290_632, 0).saturating_mul(m.into())) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(m.into()))) .saturating_add(RocksDbWeight::get().writes(5_u64)) @@ -697,21 +691,21 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `103` // Estimated: `3593` - // Minimum execution time: 43_945_000 picoseconds. - Weight::from_parts(45_249_000, 3593) + // Minimum execution time: 39_939_000 picoseconds. + Weight::from_parts(40_788_000, 3593) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:0) /// Proof: `Broker::Status` (`max_values`: Some(1), `max_size`: Some(18), added: 513, mode: `MaxEncodedLen`) /// Storage: `Broker::Regions` (r:1 w:1) - /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Proof: `Broker::Regions` (`max_values`: None, `max_size`: Some(86), added: 2561, mode: `MaxEncodedLen`) fn drop_region() -> Weight { // Proof Size summary in bytes: - // Measured: `603` - // Estimated: `3550` - // Minimum execution time: 30_680_000 picoseconds. - Weight::from_parts(32_995_000, 3550) + // Measured: `604` + // Estimated: `3551` + // Minimum execution time: 31_709_000 picoseconds. + Weight::from_parts(37_559_000, 3551) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -725,8 +719,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `601` // Estimated: `3533` - // Minimum execution time: 48_053_000 picoseconds. - Weight::from_parts(51_364_000, 3533) + // Minimum execution time: 42_895_000 picoseconds. + Weight::from_parts(53_945_000, 3533) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -742,8 +736,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `995` // Estimated: `3593` - // Minimum execution time: 57_372_000 picoseconds. - Weight::from_parts(59_466_000, 3593) + // Minimum execution time: 50_770_000 picoseconds. + Weight::from_parts(63_117_000, 3593) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -755,8 +749,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `661` // Estimated: `4698` - // Minimum execution time: 27_768_000 picoseconds. - Weight::from_parts(29_000_000, 4698) + // Minimum execution time: 33_396_000 picoseconds. + Weight::from_parts(36_247_000, 4698) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -765,20 +759,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_588_000 picoseconds. - Weight::from_parts(5_201_705, 0) + // Minimum execution time: 3_625_000 picoseconds. + Weight::from_parts(4_011_396, 0) } /// Storage: `Broker::CoreCountInbox` (r:1 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn process_core_count(n: u32, ) -> Weight { + fn process_core_count(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `404` // Estimated: `1487` - // Minimum execution time: 6_889_000 picoseconds. - Weight::from_parts(7_380_363, 1487) - // Standard Error: 21 - .saturating_add(Weight::from_parts(63, 0).saturating_mul(n.into())) + // Minimum execution time: 6_217_000 picoseconds. + Weight::from_parts(6_608_394, 1487) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -796,8 +788,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `972` // Estimated: `4437` - // Minimum execution time: 50_156_000 picoseconds. - Weight::from_parts(51_610_000, 4437) + // Minimum execution time: 46_853_000 picoseconds. + Weight::from_parts(47_740_000, 4437) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -812,14 +804,12 @@ impl WeightInfo for () { /// Storage: `Broker::Workplan` (r:0 w:10) /// Proof: `Broker::Workplan` (`max_values`: None, `max_size`: Some(1216), added: 3691, mode: `MaxEncodedLen`) /// The range of component `n` is `[0, 1000]`. - fn rotate_sale(n: u32, ) -> Weight { + fn rotate_sale(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `6281` // Estimated: `8499` - // Minimum execution time: 38_246_000 picoseconds. - Weight::from_parts(40_008_850, 8499) - // Standard Error: 94 - .saturating_add(Weight::from_parts(964, 0).saturating_mul(n.into())) + // Minimum execution time: 34_240_000 picoseconds. + Weight::from_parts(35_910_175, 8499) .saturating_add(RocksDbWeight::get().reads(5_u64)) .saturating_add(RocksDbWeight::get().writes(15_u64)) } @@ -831,8 +821,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `180` // Estimated: `3493` - // Minimum execution time: 7_962_000 picoseconds. - Weight::from_parts(8_313_000, 3493) + // Minimum execution time: 7_083_000 picoseconds. + Weight::from_parts(7_336_000, 3493) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -844,8 +834,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1423` // Estimated: `4681` - // Minimum execution time: 17_457_000 picoseconds. - Weight::from_parts(18_387_000, 4681) + // Minimum execution time: 15_029_000 picoseconds. + Weight::from_parts(15_567_000, 4681) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -853,8 +843,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 133_000 picoseconds. - Weight::from_parts(149_000, 0) + // Minimum execution time: 123_000 picoseconds. + Weight::from_parts(136_000, 0) } /// Storage: `Broker::CoreCountInbox` (r:0 w:1) /// Proof: `Broker::CoreCountInbox` (`max_values`: Some(1), `max_size`: Some(2), added: 497, mode: `MaxEncodedLen`) @@ -862,8 +852,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_407_000 picoseconds. - Weight::from_parts(2_634_000, 0) + // Minimum execution time: 1_775_000 picoseconds. + Weight::from_parts(1_911_000, 0) .saturating_add(RocksDbWeight::get().writes(1_u64)) } /// Storage: `Broker::Status` (r:1 w:1) @@ -878,8 +868,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `603` // Estimated: `4068` - // Minimum execution time: 13_043_000 picoseconds. - Weight::from_parts(13_541_000, 4068) + // Minimum execution time: 11_859_000 picoseconds. + Weight::from_parts(12_214_000, 4068) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -889,8 +879,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `239` // Estimated: `1526` - // Minimum execution time: 6_606_000 picoseconds. - Weight::from_parts(6_964_000, 1526) + // Minimum execution time: 5_864_000 picoseconds. + Weight::from_parts(6_231_000, 1526) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } From d6857aa50d1257f0b432492c1e0211b5f4062c84 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 22:19:26 +0200 Subject: [PATCH 32/33] Change bump to minor again to make CI happy. --- prdoc/pr_4521.prdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index fbe1fcdac363..99a34fe5d402 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -20,5 +20,5 @@ doc: crates: - name: pallet-broker - bump: major + bump: minor From d179b8995075f5d43ffc15f1e5976f276722cba7 Mon Sep 17 00:00:00 2001 From: eskimor Date: Wed, 29 May 2024 22:53:36 +0200 Subject: [PATCH 33/33] prdoc fix --- prdoc/pr_4521.prdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/prdoc/pr_4521.prdoc b/prdoc/pr_4521.prdoc index 99a34fe5d402..a8b42a2c7ee3 100644 --- a/prdoc/pr_4521.prdoc +++ b/prdoc/pr_4521.prdoc @@ -21,4 +21,8 @@ doc: crates: - name: pallet-broker bump: minor + - name: coretime-rococo-runtime + bump: minor + - name: coretime-westend-runtime + bump: minor