1use 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
47pub 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 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 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 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 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 let m = max as f64;
185 let block_weight = (normal as f64).min(m);
187 let v: f64 = AdjustmentVariable::get().to_float();
188
189 let ss = target as f64;
191 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 Multiplier::from_inner(100),
232 );
233 })
234 })
235 }
236
237 #[test]
238 fn multiplier_can_grow_from_zero() {
239 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 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 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 run_with_system_weight(Weight::zero(), || {
275 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 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 let block_weight = BlockWeights::get().get(DispatchClass::Normal).max_total.unwrap() -
311 Weight::from_parts(100, 0);
312
313 let tx_weight = frame_support::weights::constants::ExtrinsicBaseWeight::get();
315
316 run_with_system_weight(block_weight, || {
317 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 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 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 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 assert_eq!(next, fm)
383 });
384 run_with_system_weight(target() * 2, || {
385 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 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 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 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 vec![
442 Weight::zero(),
443 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 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 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 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 assert_eq!(fm, max_fm);
490 })
491 });
492 }
493}