referrerpolicy=no-referrer-when-downgrade

kitchensink_runtime/
impls.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Some configurable implementations as associated type for the substrate runtime.
19
20use alloc::boxed::Box;
21use frame_support::{
22	pallet_prelude::*,
23	traits::{
24		fungibles::{Balanced, Credit},
25		Currency, OnUnbalanced,
26	},
27};
28use pallet_alliance::{IdentityVerifier, ProposalIndex, ProposalProvider};
29use pallet_asset_tx_payment::HandleCredit;
30use pallet_identity::legacy::IdentityField;
31use polkadot_sdk::*;
32
33use crate::{
34	AccountId, AllianceCollective, AllianceMotion, Assets, Authorship, Balances, Hash,
35	NegativeImbalance, Runtime, RuntimeCall,
36};
37
38pub struct Author;
39impl OnUnbalanced<NegativeImbalance> for Author {
40	fn on_nonzero_unbalanced(amount: NegativeImbalance) {
41		if let Some(author) = Authorship::author() {
42			Balances::resolve_creating(&author, amount);
43		}
44	}
45}
46
47/// A `HandleCredit` implementation that naively transfers the fees to the block author.
48/// Will drop and burn the assets in case the transfer fails.
49pub struct CreditToBlockAuthor;
50impl HandleCredit<AccountId, Assets> for CreditToBlockAuthor {
51	fn handle_credit(credit: Credit<AccountId, Assets>) {
52		if let Some(author) = pallet_authorship::Pallet::<Runtime>::author() {
53			// Drop the result which will trigger the `OnDrop` of the imbalance in case of error.
54			let _ = Assets::resolve(&author, credit);
55		}
56	}
57}
58
59pub struct AllianceIdentityVerifier;
60impl IdentityVerifier<AccountId> for AllianceIdentityVerifier {
61	fn has_required_identities(who: &AccountId) -> bool {
62		crate::Identity::has_identity(who, (IdentityField::Display | IdentityField::Web).bits())
63	}
64
65	fn has_good_judgement(who: &AccountId) -> bool {
66		use pallet_identity::{IdentityOf, Judgement};
67		IdentityOf::<Runtime>::get(who)
68			.map(|registration| registration.judgements)
69			.map_or(false, |judgements| {
70				judgements
71					.iter()
72					.any(|(_, j)| matches!(j, Judgement::KnownGood | Judgement::Reasonable))
73			})
74	}
75
76	fn super_account_id(who: &AccountId) -> Option<AccountId> {
77		use pallet_identity::SuperOf;
78		SuperOf::<Runtime>::get(who).map(|parent| parent.0)
79	}
80}
81
82pub struct AllianceProposalProvider;
83impl ProposalProvider<AccountId, Hash, RuntimeCall> for AllianceProposalProvider {
84	fn propose_proposal(
85		who: AccountId,
86		threshold: u32,
87		proposal: Box<RuntimeCall>,
88		length_bound: u32,
89	) -> Result<(u32, u32), DispatchError> {
90		AllianceMotion::do_propose_proposed(who, threshold, proposal, length_bound)
91	}
92
93	fn vote_proposal(
94		who: AccountId,
95		proposal: Hash,
96		index: ProposalIndex,
97		approve: bool,
98	) -> Result<bool, DispatchError> {
99		AllianceMotion::do_vote(who, proposal, index, approve)
100	}
101
102	fn close_proposal(
103		proposal_hash: Hash,
104		proposal_index: ProposalIndex,
105		proposal_weight_bound: Weight,
106		length_bound: u32,
107	) -> DispatchResultWithPostInfo {
108		AllianceMotion::do_close(proposal_hash, proposal_index, proposal_weight_bound, length_bound)
109	}
110
111	fn proposal_of(proposal_hash: Hash) -> Option<RuntimeCall> {
112		pallet_collective::ProposalOf::<Runtime, AllianceCollective>::get(proposal_hash)
113	}
114}
115
116#[cfg(test)]
117mod multiplier_tests {
118	use frame_support::{
119		dispatch::DispatchClass,
120		weights::{Weight, WeightToFee},
121	};
122	use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment};
123	use polkadot_sdk::*;
124	use sp_runtime::{
125		assert_eq_error_rate,
126		traits::{Convert, One, Zero},
127		BuildStorage, FixedPointNumber,
128	};
129
130	use crate::{
131		constants::{currency::*, time::*},
132		AdjustmentVariable, MaximumMultiplier, MinimumMultiplier, Runtime,
133		RuntimeBlockWeights as BlockWeights, System, TargetBlockFullness, TransactionPayment,
134	};
135
136	fn max_normal() -> Weight {
137		BlockWeights::get()
138			.get(DispatchClass::Normal)
139			.max_total
140			.unwrap_or_else(|| BlockWeights::get().max_block)
141	}
142
143	fn min_multiplier() -> Multiplier {
144		MinimumMultiplier::get()
145	}
146
147	fn target() -> Weight {
148		TargetBlockFullness::get() * max_normal()
149	}
150
151	// update based on runtime impl.
152	fn runtime_multiplier_update(fm: Multiplier) -> Multiplier {
153		TargetedFeeAdjustment::<
154			Runtime,
155			TargetBlockFullness,
156			AdjustmentVariable,
157			MinimumMultiplier,
158			MaximumMultiplier,
159		>::convert(fm)
160	}
161
162	// update based on reference impl.
163	fn truth_value_update(block_weight: Weight, previous: Multiplier) -> Multiplier {
164		let accuracy = Multiplier::accuracy() as f64;
165		let previous_float = previous.into_inner() as f64 / accuracy;
166		// bump if it is zero.
167		let previous_float = previous_float.max(min_multiplier().into_inner() as f64 / accuracy);
168
169		let max_normal = max_normal();
170		let target_weight = target();
171		let normalized_weight_dimensions = (
172			block_weight.ref_time() as f64 / max_normal.ref_time() as f64,
173			block_weight.proof_size() as f64 / max_normal.proof_size() as f64,
174		);
175
176		let (normal, max, target) =
177			if normalized_weight_dimensions.0 < normalized_weight_dimensions.1 {
178				(block_weight.proof_size(), max_normal.proof_size(), target_weight.proof_size())
179			} else {
180				(block_weight.ref_time(), max_normal.ref_time(), target_weight.ref_time())
181			};
182
183		// maximum tx weight
184		let m = max as f64;
185		// block weight always truncated to max weight
186		let block_weight = (normal as f64).min(m);
187		let v: f64 = AdjustmentVariable::get().to_float();
188
189		// Ideal saturation in terms of weight
190		let ss = target as f64;
191		// Current saturation in terms of weight
192		let s = block_weight;
193
194		let t1 = v * (s / m - ss / m);
195		let t2 = v.powi(2) * (s / m - ss / m).powi(2) / 2.0;
196		let next_float = previous_float * (1.0 + t1 + t2);
197		Multiplier::from_float(next_float)
198	}
199
200	fn run_with_system_weight<F>(w: Weight, assertions: F)
201	where
202		F: Fn() -> (),
203	{
204		let mut t: sp_io::TestExternalities = frame_system::GenesisConfig::<Runtime>::default()
205			.build_storage()
206			.unwrap()
207			.into();
208		t.execute_with(|| {
209			System::set_block_consumed_resources(w, 0);
210			assertions()
211		});
212	}
213
214	#[test]
215	fn truth_value_update_poc_works() {
216		let fm = Multiplier::saturating_from_rational(1, 2);
217		let test_set = vec![
218			(Weight::zero(), fm),
219			(Weight::from_parts(100, 0), fm),
220			(Weight::from_parts(1000, 0), fm),
221			(target(), fm),
222			(max_normal() / 2, fm),
223			(max_normal(), fm),
224		];
225		test_set.into_iter().for_each(|(w, fm)| {
226			run_with_system_weight(w, || {
227				assert_eq_error_rate!(
228					truth_value_update(w, fm),
229					runtime_multiplier_update(fm),
230					// Error is only 1 in 100^18
231					Multiplier::from_inner(100),
232				);
233			})
234		})
235	}
236
237	#[test]
238	fn multiplier_can_grow_from_zero() {
239		// if the min is too small, then this will not change, and we are doomed forever.
240		// the block ref time is 1/100th bigger than target.
241		run_with_system_weight(target().set_ref_time(target().ref_time() * 101 / 100), || {
242			let next = runtime_multiplier_update(min_multiplier());
243			assert!(next > min_multiplier(), "{:?} !> {:?}", next, min_multiplier());
244		});
245
246		// the block proof size is 1/100th bigger than target.
247		run_with_system_weight(target().set_proof_size((target().proof_size() / 100) * 101), || {
248			let next = runtime_multiplier_update(min_multiplier());
249			assert!(next > min_multiplier(), "{:?} !> {:?}", next, min_multiplier());
250		})
251	}
252
253	#[test]
254	fn multiplier_cannot_go_below_limit() {
255		// will not go any further below even if block is empty.
256		run_with_system_weight(Weight::zero(), || {
257			let next = runtime_multiplier_update(min_multiplier());
258			assert_eq!(next, min_multiplier());
259		})
260	}
261
262	#[test]
263	fn time_to_reach_zero() {
264		// blocks per 24h in substrate-node: 28,800 (k)
265		// s* = 0.1875
266		// The bound from the research in an empty chain is:
267		// v <~ (p / k(0 - s*))
268		// p > v * k * -0.1875
269		// to get p == -1 we'd need
270		// -1 > 0.00001 * k * -0.1875
271		// 1 < 0.00001 * k * 0.1875
272		// 10^9 / 1875 < k
273		// k > 533_333 ~ 18,5 days.
274		run_with_system_weight(Weight::zero(), || {
275			// start from 1, the default.
276			let mut fm = Multiplier::one();
277			let mut iterations: u64 = 0;
278			loop {
279				let next = runtime_multiplier_update(fm);
280				fm = next;
281				if fm == min_multiplier() {
282					break;
283				}
284				iterations += 1;
285			}
286			assert!(iterations > 533_333);
287		})
288	}
289
290	#[test]
291	fn min_change_per_day() {
292		run_with_system_weight(max_normal(), || {
293			let mut fm = Multiplier::one();
294			// See the example in the doc of `TargetedFeeAdjustment`. are at least 0.234, hence
295			// `fm > 1.234`.
296			for _ in 0..DAYS {
297				let next = runtime_multiplier_update(fm);
298				fm = next;
299			}
300			assert!(fm > Multiplier::saturating_from_rational(1234, 1000));
301		})
302	}
303
304	#[test]
305	#[ignore]
306	fn congested_chain_simulation() {
307		// `cargo test congested_chain_simulation -- --nocapture` to get some insight.
308
309		// almost full. The entire quota of normal transactions is taken.
310		let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() -
311			Weight::from_parts(100, 0);
312
313		// Default substrate weight.
314		let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get();
315
316		run_with_system_weight(block_weight, || {
317			// initial value configured on module
318			let mut fm = Multiplier::one();
319			assert_eq!(fm, TransactionPayment::next_fee_multiplier());
320
321			let mut iterations: u64 = 0;
322			loop {
323				let next = runtime_multiplier_update(fm);
324				// if no change, panic. This should never happen in this case.
325				if fm == next {
326					panic!("The fee should ever increase");
327				}
328				fm = next;
329				iterations += 1;
330				let fee =
331					<Runtime as pallet_transaction_payment::Config>::WeightToFee::weight_to_fee(
332						&tx_weight,
333					);
334				let adjusted_fee = fm.saturating_mul_acc_int(fee);
335				println!(
336					"iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \
337					{} cents, {} dollars",
338					iterations,
339					fm,
340					adjusted_fee,
341					adjusted_fee / MILLICENTS,
342					adjusted_fee / CENTS,
343					adjusted_fee / DOLLARS,
344				);
345			}
346		});
347	}
348
349	#[test]
350	fn stateless_weight_mul() {
351		let fm = Multiplier::saturating_from_rational(1, 2);
352		run_with_system_weight(target() / 4, || {
353			let next = runtime_multiplier_update(fm);
354			assert_eq_error_rate!(
355				next,
356				truth_value_update(target() / 4, fm),
357				Multiplier::from_inner(100),
358			);
359
360			// Light block. Multiplier is reduced a little.
361			assert!(next < fm);
362		});
363
364		run_with_system_weight(target() / 2, || {
365			let next = runtime_multiplier_update(fm);
366			assert_eq_error_rate!(
367				next,
368				truth_value_update(target() / 2, fm),
369				Multiplier::from_inner(100),
370			);
371			// Light block. Multiplier is reduced a little.
372			assert!(next < fm);
373		});
374		run_with_system_weight(target(), || {
375			let next = runtime_multiplier_update(fm);
376			assert_eq_error_rate!(
377				next,
378				truth_value_update(target(), fm),
379				Multiplier::from_inner(100),
380			);
381			// ideal. No changes.
382			assert_eq!(next, fm)
383		});
384		run_with_system_weight(target() * 2, || {
385			// More than ideal. Fee is increased.
386			let next = runtime_multiplier_update(fm);
387			assert_eq_error_rate!(
388				next,
389				truth_value_update(target() * 2, fm),
390				Multiplier::from_inner(100),
391			);
392
393			// Heavy block. Fee is increased a little.
394			assert!(next > fm);
395		});
396	}
397
398	#[test]
399	fn weight_mul_grow_on_big_block() {
400		run_with_system_weight(target() * 2, || {
401			let mut original = Multiplier::zero();
402			let mut next = Multiplier::default();
403
404			(0..1_000).for_each(|_| {
405				next = runtime_multiplier_update(original);
406				assert_eq_error_rate!(
407					next,
408					truth_value_update(target() * 2, original),
409					Multiplier::from_inner(100),
410				);
411				// must always increase
412				assert!(next > original, "{:?} !>= {:?}", next, original);
413				original = next;
414			});
415		});
416	}
417
418	#[test]
419	fn weight_mul_decrease_on_small_block() {
420		run_with_system_weight(target() / 2, || {
421			let mut original = Multiplier::saturating_from_rational(1, 2);
422			let mut next;
423
424			for _ in 0..100 {
425				// decreases
426				next = runtime_multiplier_update(original);
427				assert!(next < original, "{:?} !<= {:?}", next, original);
428				original = next;
429			}
430		})
431	}
432
433	#[test]
434	fn weight_to_fee_should_not_overflow_on_large_weights() {
435		let kb_time = Weight::from_parts(1024, 0);
436		let kb_size = Weight::from_parts(0, 1024);
437		let mb_time = 1024u64 * kb_time;
438		let max_fm = Multiplier::saturating_from_integer(i128::MAX);
439
440		// check that for all values it can compute, correctly.
441		vec![
442			Weight::zero(),
443			// testcases ignoring proof size part of the weight.
444			Weight::from_parts(1, 0),
445			Weight::from_parts(10, 0),
446			Weight::from_parts(1000, 0),
447			kb_time,
448			10u64 * kb_time,
449			100u64 * kb_time,
450			mb_time,
451			10u64 * mb_time,
452			Weight::from_parts(2147483647, 0),
453			Weight::from_parts(4294967295, 0),
454			// testcases ignoring ref time part of the weight.
455			Weight::from_parts(0, 100000000000),
456			1000000u64 * kb_size,
457			1000000000u64 * kb_size,
458			Weight::from_parts(0, 18014398509481983),
459			Weight::from_parts(0, 9223372036854775807),
460			// test cases with both parts of the weight.
461			BlockWeights::get().max_block / 1024,
462			BlockWeights::get().max_block / 2,
463			BlockWeights::get().max_block,
464			Weight::MAX / 2,
465			Weight::MAX,
466		]
467		.into_iter()
468		.for_each(|i| {
469			run_with_system_weight(i, || {
470				let next = runtime_multiplier_update(Multiplier::one());
471				let truth = truth_value_update(i, Multiplier::one());
472				assert_eq_error_rate!(truth, next, Multiplier::from_inner(50_000_000));
473			});
474		});
475
476		// Some values that are all above the target and will cause an increase.
477		let t = target();
478		vec![
479			t + Weight::from_parts(100, 0),
480			t + Weight::from_parts(0, t.proof_size() * 2),
481			t * 2,
482			t * 4,
483		]
484		.into_iter()
485		.for_each(|i| {
486			run_with_system_weight(i, || {
487				let fm = runtime_multiplier_update(max_fm);
488				// won't grow. The convert saturates everything.
489				assert_eq!(fm, max_fm);
490			})
491		});
492	}
493}